mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-21 00:01:48 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43f038e9ee | ||
|
|
cb09d5c540 | ||
|
|
d29f607ab0 | ||
|
|
dc2f7f493c | ||
|
|
452206729b | ||
|
|
a8d41f67c9 | ||
|
|
84cb7e6446 | ||
|
|
6c21245b98 | ||
|
|
a1121c165f | ||
|
|
5836f88ef0 | ||
|
|
41cf8ae3e2 | ||
|
|
c1ace0bdd5 | ||
|
|
66e682ba3d | ||
|
|
0de3deebf7 | ||
|
|
0c1859f848 | ||
|
|
b8910a3b64 | ||
|
|
b2e7c529a0 | ||
|
|
977060e927 | ||
|
|
f609471fd2 | ||
|
|
a1a94d27db | ||
|
|
e12faaba27 | ||
|
|
6f5f7c4dd3 | ||
|
|
e1800b8d50 | ||
|
|
7779533c1e | ||
|
|
74cefc00f7 | ||
|
|
a8d33b6a30 | ||
|
|
f83869da04 | ||
|
|
815710c081 | ||
|
|
805c516371 | ||
|
|
b85d929890 | ||
|
|
dd9ce932fc | ||
|
|
3667243fc7 | ||
|
|
92f4ab8ef5 | ||
|
|
90ca095954 | ||
|
|
fa63d8dc4f | ||
|
|
fc134b146a | ||
|
|
3abf106496 | ||
|
|
a17534abcb | ||
|
|
4614886954 | ||
|
|
26a91dc7e9 | ||
|
|
cac5180c76 |
No files matched your search
+2
-1
@@ -337,4 +337,5 @@ ASALocalRun/
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
healthchecksdb
|
||||
*.clef
|
||||
+15
-5
@@ -1,9 +1,11 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30128.74
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32014.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorControl", "ColorControl\ColorControl.csproj", "{A6005D08-70F0-44CB-A384-9219EBA84F21}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ColorControl", "ColorControl\ColorControl.csproj", "{A6005D08-70F0-44CB-A384-9219EBA84F21}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nspector", "Nspector\Nspector.csproj", "{76797290-48F5-4A82-86C3-FAD25DAE4500}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -15,12 +17,20 @@ Global
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.Build.0 = Debug|x64
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|x64.ActiveCfg = Release|x64
|
||||
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|x64.Build.0 = Release|x64
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"Build":"0.3.261602\u002Befb3cc30a24e699e6e4337f263a2b9478f04f618","CurrentProject":"ColorControl.csproj","EntryPoints":["ColorControl.csproj"],"Properties":{}}
|
||||
@@ -0,0 +1,317 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Dependency Analysis",
|
||||
"semanticVersion": "0.3.261602",
|
||||
"informationUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview",
|
||||
"rules": [
|
||||
{
|
||||
"id": "UA103",
|
||||
"name": "ReferenceToBeDeleted",
|
||||
"fullDescription": {
|
||||
"text": "References that need to be deleted in order to upgrade the project to chosen TFM"
|
||||
},
|
||||
"helpUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview"
|
||||
},
|
||||
{
|
||||
"id": "UA106",
|
||||
"name": "PackageToBeAdded",
|
||||
"fullDescription": {
|
||||
"text": "Packages that need to be added in order to upgrade the project to chosen TFM"
|
||||
},
|
||||
"helpUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview"
|
||||
},
|
||||
{
|
||||
"id": "UA108",
|
||||
"name": "FrameworkReferenceToBeAdded",
|
||||
"fullDescription": {
|
||||
"text": "Framework Reference that need to be added in order to upgrade the project to chosen TFM"
|
||||
},
|
||||
"helpUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "UA103",
|
||||
"message": {
|
||||
"text": "Reference to System.Configuration needs to be deleted."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA103",
|
||||
"message": {
|
||||
"text": "Reference to System.ServiceModel needs to be deleted."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA103",
|
||||
"message": {
|
||||
"text": "Reference to System.Web needs to be deleted."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA103",
|
||||
"message": {
|
||||
"text": "Reference to System.Web.Extensions needs to be deleted."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.Configuration.ConfigurationManager, Version=5.0.0 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.Primitives, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.Http, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.Duplex, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.NetTcp, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.Security, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package System.ServiceModel.Federation, Version=4.8.1 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, Version=0.3.261602 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA106",
|
||||
"message": {
|
||||
"text": "Package Microsoft.Windows.Compatibility, Version=6.0.0 needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "UA108",
|
||||
"message": {
|
||||
"text": "Framework Reference to Microsoft.AspNetCore.App needs to be added."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/ColorControl.csproj"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"columnKind": "utf16CodeUnits"
|
||||
},
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "API Upgradability",
|
||||
"semanticVersion": "0.3.261602",
|
||||
"informationUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview"
|
||||
}
|
||||
},
|
||||
"results": [],
|
||||
"columnKind": "utf16CodeUnits"
|
||||
},
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Component Analysis",
|
||||
"semanticVersion": "0.3.261602",
|
||||
"informationUri": "https://docs.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview",
|
||||
"rules": [
|
||||
{
|
||||
"id": "UA202",
|
||||
"name": "Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.WinformsDpiSettingUpdater",
|
||||
"fullDescription": {
|
||||
"text": "Winforms Source Updater"
|
||||
},
|
||||
"helpUri": "about:blank"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "UA202",
|
||||
"message": {
|
||||
"text": "HighDpiMode needs to set in Main() instead of app.config or app.manifest - Application.SetHighDpiMode(HighDpiMode.<setting>). It is recommended to use SystemAware as the HighDpiMode option for better results."
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "file:///C:/Users/vinni/source/repos/ColorControl/ColorControl/Program.cs"
|
||||
},
|
||||
"region": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"columnKind": "utf16CodeUnits"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="PacketDotNet" publicKeyToken="451414c7667b2a58" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<System.Windows.Forms.ApplicationConfigurationSection>
|
||||
<add key="DpiAwareness" value="PerMonitorV2" />
|
||||
</System.Windows.Forms.ApplicationConfigurationSection>
|
||||
</configuration>
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace ColorControl
|
||||
{
|
||||
public class AppContext
|
||||
{
|
||||
public static AppContext CurrentContext { get; private set; }
|
||||
|
||||
public Config Config { get; private set; }
|
||||
|
||||
public StartUpParams StartUpParams { get; private set; }
|
||||
|
||||
public AppContext(Config config, StartUpParams startUpParams)
|
||||
{
|
||||
Config = config;
|
||||
StartUpParams = startUpParams;
|
||||
|
||||
CurrentContext = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,293 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A6005D08-70F0-44CB-A384-9219EBA84F21}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>ColorControl</RootNamespace>
|
||||
<AssemblyName>ColorControl</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>false</MapFileExtensions>
|
||||
<UpdateUrl>https://www.maassoft.com/downloads/ColorControl/</UpdateUrl>
|
||||
<ProductName>ColorControl</ProductName>
|
||||
<PublisherName>Maassoft</PublisherName>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>2.0.0.0</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<TargetFramework>net6.0-windows10.0.20348.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>false</MapFileExtensions>
|
||||
<UpdateUrl>https://www.maassoft.com/downloads/ColorControl/</UpdateUrl>
|
||||
<ProductName>ColorControl</ProductName>
|
||||
<PublisherName>Maassoft</PublisherName>
|
||||
<Company>Maassoft</Company>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>8.2.0.0</ApplicationVersion>
|
||||
<Version>8.2.0.0</Version>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>false</BootstrapperEnabled>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>false</UseWPF>
|
||||
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
||||
<ApplicationIcon>pngbarn.ico</ApplicationIcon>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
<Copyright>2020-2022 Maassoft</Copyright>
|
||||
<Authors>Maassoft</Authors>
|
||||
<ManifestCertificateThumbprint>CF0BC153EA664CD97AC6F234B91F45D34630958F</ManifestCertificateThumbprint>
|
||||
<ManifestKeyFile>ColorControl_1_TemporaryKey.pfx</ManifestKeyFile>
|
||||
<GenerateManifests>true</GenerateManifests>
|
||||
<TargetZone>LocalIntranet</TargetZone>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
<SignManifests>true</SignManifests>
|
||||
<AssemblyOriginatorKeyFile>ColorControl_TemporaryKey.pfx</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestCertificateThumbprint>482C987E8C69872608C9D44751E004C5784E8540</ManifestCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestKeyFile>ColorControl_TemporaryKey.pfx</ManifestKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<GenerateManifests>true</GenerateManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetZone>LocalIntranet</TargetZone>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignManifests>true</SignManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>ColorControl_TemporaryKey.pfx</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>pngbarn.ico</ApplicationIcon>
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.9.1.0, Culture=neutral, PublicKeyToken=e25603a88b3aa7da, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\TaskScheduler.2.9.1\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Native, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Native.0.1.0\lib\net35\Native.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.7.9\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NStandard, Version=0.6.12.5, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NStandard.0.6.12.5\lib\net46\NStandard.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NvAPIWrapper, Version=0.8.1.100, Culture=neutral, PublicKeyToken=310fd07b25df79b3, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NvAPIWrapper.Net.0.8.1.101\lib\net45\NvAPIWrapper.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NWin32, Version=1.0.7.6, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NWin32.1.0.7.6\lib\net46\NWin32.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PacketDotNet, Version=1.2.0.0, Culture=neutral, PublicKeyToken=451414c7667b2a58, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PacketDotNet.1.2.0\lib\net45\PacketDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="SharpPcap, Version=5.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SharpPcap.5.4.0\lib\netstandard2.0\SharpPcap.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Windows">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.18362.0\Windows.winmd</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Services\Common\QuickAccessForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Update="services\lg\RemoteControlPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Resources\LG_register.json" />
|
||||
<EmbeddedResource Include="Resources\RC_coords.txt" />
|
||||
<EmbeddedResource Include="Resources\LG_remote.jpg" />
|
||||
<Content Include="LgPresets.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ADL.cs" />
|
||||
<Compile Include="ADLWrapper.cs" />
|
||||
<Compile Include="AmdDisplayInfo.cs" />
|
||||
<Compile Include="UserSessionInfo.cs" />
|
||||
<Compile Include="ServiceBase.cs" />
|
||||
<Compile Include="NvDisplayInfo.cs" />
|
||||
<Compile Include="MessageForms.cs" />
|
||||
<Compile Include="GraphicsService.cs" />
|
||||
<Compile Include="AmdService.cs" />
|
||||
<Compile Include="AmdPreset.cs" />
|
||||
<Compile Include="RemoteControlForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="RemoteControlForm.Designer.cs">
|
||||
<DependentUpon>RemoteControlForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="StartUpParams.cs" />
|
||||
<Compile Include="LgServiceConfig.cs" />
|
||||
<Compile Include="WOL.cs" />
|
||||
<Compile Include="RestartDetector.cs" />
|
||||
<Compile Include="ExceptionExtensions.cs" />
|
||||
<Compile Include="lgtv\App.cs" />
|
||||
<Compile Include="lgtv\Channel.cs" />
|
||||
<Compile Include="lgtv\ClientKeyStore.cs" />
|
||||
<Compile Include="lgtv\ExternalInput.cs" />
|
||||
<Compile Include="lgtv\IsConnectedDelegate.cs" />
|
||||
<Compile Include="lgtv\LgTvApi.cs" />
|
||||
<Compile Include="lgtv\LgTvConnection.cs" />
|
||||
<Compile Include="lgtv\LgWebOsMouseService.cs" />
|
||||
<Compile Include="lgtv\MessageType.cs" />
|
||||
<Compile Include="lgtv\RawRequestMessage.cs" />
|
||||
<Compile Include="lgtv\RequestMessage.cs" />
|
||||
<Compile Include="PnpDev.cs" />
|
||||
<Compile Include="TreeNodeBuilder.cs" />
|
||||
<Compile Include="ColorDataConverter.cs" />
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="lgtv\LgApp.cs" />
|
||||
<Compile Include="PresetBase.cs" />
|
||||
<Compile Include="LgPreset.cs" />
|
||||
<Compile Include="NvPreset.cs" />
|
||||
<Compile Include="LgService.cs" />
|
||||
<Compile Include="NvService.cs" />
|
||||
<Compile Include="ScreenInterrogatory.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="RemoteControlForm.resx">
|
||||
<DependentUpon>RemoteControlForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="ColorControl_TemporaryKey.pfx" />
|
||||
<Content Include="LgPresets.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="lgtv\README.md" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\app.manifest" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Content Include="pngbarn.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<Folder Include="bin\x64\Debug\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="pngbarn.ico" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="bin\x64\Debug\" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NLog" Version="5.0.4" />
|
||||
<PackageReference Include="NStandard" Version="0.15.2" />
|
||||
<PackageReference Include="NvAPIWrapper.Net" Version="0.8.1.101" />
|
||||
<PackageReference Include="NWin32" Version="1.2.3" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.4.9" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<ProjectReference Include="..\Nspector\Nspector.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,54 +0,0 @@
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
public class ColorDataConverter : JavaScriptConverter
|
||||
{
|
||||
public override IEnumerable<Type> SupportedTypes
|
||||
{
|
||||
get { return new List<Type>() { typeof(ColorData) }; }
|
||||
}
|
||||
|
||||
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
|
||||
{
|
||||
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||
if (obj == null) return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
|
||||
{
|
||||
var format = ColorDataFormat.RGB;
|
||||
var dynamicRange = ColorDataDynamicRange.VESA;
|
||||
var colorDepth = ColorDataDepth.BPC8;
|
||||
var colorimetry = ColorDataColorimetry.Auto;
|
||||
var selectionPolicy = ColorDataSelectionPolicy.User;
|
||||
object value;
|
||||
if (dictionary.TryGetValue("ColorFormat", out value))
|
||||
{
|
||||
format = (ColorDataFormat)Enum.ToObject(typeof(ColorDataFormat), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("ColorDepth", out value))
|
||||
{
|
||||
colorDepth = (ColorDataDepth)Enum.ToObject(typeof(ColorDataDepth), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("Colorimetry", out value))
|
||||
{
|
||||
colorimetry = (ColorDataColorimetry)Enum.ToObject(typeof(ColorDataColorimetry), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("DynamicRange", out value))
|
||||
{
|
||||
dynamicRange = (ColorDataDynamicRange)Enum.ToObject(typeof(ColorDataDynamicRange), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out value))
|
||||
{
|
||||
selectionPolicy = (ColorDataSelectionPolicy)Enum.ToObject(typeof(ColorDataSelectionPolicy), value);
|
||||
}
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy, desktopColorDepth: ColorDataDesktopDepth.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -18,7 +18,7 @@ namespace ColorControl
|
||||
/// <param name="environmentStackTrace">Environment stack trace, for pulling additional stack frames.</param>
|
||||
public static string ToLogString(this Exception exception, string environmentStackTrace = "")
|
||||
{
|
||||
List<string> environmentStackTraceLines = ExceptionExtensions.GetUserStackTraceLines(environmentStackTrace);
|
||||
List<string> environmentStackTraceLines = GetUserStackTraceLines(environmentStackTrace);
|
||||
if (environmentStackTraceLines.Any())
|
||||
{
|
||||
environmentStackTraceLines.RemoveAt(0);
|
||||
@@ -29,7 +29,14 @@ namespace ColorControl
|
||||
|
||||
string fullStackTrace = String.Join(Environment.NewLine, stackTraceLines);
|
||||
|
||||
string logMessage = exception.Message + Environment.NewLine + fullStackTrace;
|
||||
string logMessage = exception.Message + Environment.NewLine;
|
||||
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
logMessage += $"Inner exception: {exception.InnerException.Message}{Environment.NewLine}";
|
||||
}
|
||||
|
||||
logMessage += fullStackTrace;
|
||||
return logMessage;
|
||||
}
|
||||
|
||||
@@ -51,7 +58,7 @@ namespace ColorControl
|
||||
List<string> outputList = new List<string>();
|
||||
Regex regex = new Regex(@"([^\)]*\)) in (.*):([A-z]*) (\d)*$");
|
||||
|
||||
List<string> stackTraceLines = ExceptionExtensions.GetStackTraceLines(fullStackTrace);
|
||||
List<string> stackTraceLines = GetStackTraceLines(fullStackTrace);
|
||||
foreach (string stackTraceLine in stackTraceLines)
|
||||
{
|
||||
if (!regex.IsMatch(stackTraceLine))
|
||||
@@ -0,0 +1,83 @@
|
||||
using ColorControl.Svc;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO.Pipes;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
public static class PipeUtils
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static string SendMessage(string message)
|
||||
{
|
||||
var pipeClient = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.None);
|
||||
try
|
||||
{
|
||||
pipeClient.Connect(500);
|
||||
try
|
||||
{
|
||||
var ss = new StreamString(pipeClient);
|
||||
|
||||
ss.WriteString(message);
|
||||
|
||||
var result = ss.ReadString();
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pipeClient.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "PipeUtils.SendMessage");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SvcResultMessage SendMessage(SvcMessage message)
|
||||
{
|
||||
var messageJson = JsonConvert.SerializeObject(message);
|
||||
|
||||
var resultJson = SendMessage(messageJson);
|
||||
|
||||
if (resultJson == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultMessage = JsonConvert.DeserializeObject<SvcResultMessage>(resultJson);
|
||||
|
||||
return resultMessage;
|
||||
}
|
||||
|
||||
public static SvcResultMessage SendMessage(SvcMessageType messageType)
|
||||
{
|
||||
var message = new SvcMessage { MessageType = messageType };
|
||||
|
||||
return SendMessage(message);
|
||||
}
|
||||
|
||||
public static bool? SendRpcMessage(string command)
|
||||
{
|
||||
var message = new SvcMessage { MessageType = SvcMessageType.ExecuteRpc, Data = command };
|
||||
|
||||
var messageJson = JsonConvert.SerializeObject(message);
|
||||
|
||||
var resultJson = SendMessage(messageJson);
|
||||
|
||||
if (resultJson == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultMessage = JsonConvert.DeserializeObject<SvcResultMessage>(resultJson);
|
||||
|
||||
return resultMessage.Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
private static string FindIndexedProcessName(int pid, string processName, IEnumerable<Process> processes = null)
|
||||
{
|
||||
var processesByName = processes == null ? Process.GetProcessesByName(processName) : processes.Where(p => p.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
string processIndexedName = null;
|
||||
|
||||
for (var index = 0; index < processesByName.Length; index++)
|
||||
{
|
||||
processIndexedName = index == 0 ? processName : processName + "#" + index;
|
||||
var processId = new PerformanceCounter("Process", "ID Process", processIndexedName);
|
||||
if ((int)processId.NextValue() == pid)
|
||||
{
|
||||
return processIndexedName;
|
||||
}
|
||||
}
|
||||
|
||||
return processIndexedName;
|
||||
}
|
||||
|
||||
private static Process FindPidFromIndexedProcessName(string indexedProcessName, IEnumerable<Process> processes = null)
|
||||
{
|
||||
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
|
||||
var parentProcessId = (int)parentId.NextValue();
|
||||
|
||||
try
|
||||
{
|
||||
return processes == null ? Process.GetProcessById(parentProcessId) : processes.FirstOrDefault(p => p.Id == parentProcessId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Process Parent(this Process process, IEnumerable<Process> processes = null)
|
||||
{
|
||||
var indexedProcessName = FindIndexedProcessName(process.Id, process.ProcessName, processes);
|
||||
if (indexedProcessName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return FindPidFromIndexedProcessName(indexedProcessName, processes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
public class StreamString
|
||||
{
|
||||
private Stream ioStream;
|
||||
private UnicodeEncoding streamEncoding;
|
||||
|
||||
public StreamString(Stream ioStream)
|
||||
{
|
||||
this.ioStream = ioStream;
|
||||
streamEncoding = new UnicodeEncoding();
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
var lenBytes = new byte[4];
|
||||
|
||||
var read = ioStream.Read(lenBytes, 0, lenBytes.Length);
|
||||
|
||||
if (read < lenBytes.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var len = BitConverter.ToInt32(lenBytes);
|
||||
|
||||
var inBuffer = new byte[len];
|
||||
ioStream.Read(inBuffer, 0, len);
|
||||
|
||||
return streamEncoding.GetString(inBuffer);
|
||||
}
|
||||
|
||||
public int WriteString(string outString)
|
||||
{
|
||||
var outBuffer = streamEncoding.GetBytes(outString);
|
||||
var len = outBuffer.Length;
|
||||
|
||||
var lenBytes = BitConverter.GetBytes(len);
|
||||
|
||||
ioStream.Write(lenBytes, 0, lenBytes.Length);
|
||||
ioStream.Write(outBuffer, 0, len);
|
||||
ioStream.Flush();
|
||||
|
||||
return outBuffer.Length + lenBytes.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,256 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.IpHlpApi;
|
||||
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
class WOL
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length.";
|
||||
private const string ArgumentExceptionInvalidPasswordLength = "Invalid password length.";
|
||||
private const int DefaultWolPort = 9;
|
||||
|
||||
public static bool WakeFunction(string macAddress, string ipAddress = null)
|
||||
{
|
||||
return WakeFunctionToAllNics(macAddress, ipAddress);
|
||||
}
|
||||
|
||||
public static bool WakeFunctionCheckAdmin(string macAddress, string ipAddress = null)
|
||||
{
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
return WakeFunction(macAddress, ipAddress);
|
||||
}
|
||||
|
||||
var result = PipeUtils.SendRpcMessage($"{StartUpParams.SendWakeOnLanParam} {macAddress} {ipAddress}");
|
||||
|
||||
// Execute local WOL anyway because pipe failed
|
||||
if (!result.HasValue)
|
||||
{
|
||||
result = WakeFunction(macAddress, ipAddress);
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public static bool WakeFunctionToAllNics(string macAddress, string ipAddressString)
|
||||
{
|
||||
var result = false;
|
||||
Logger.Debug($"Sending WOL packet to MAC-address {macAddress}");
|
||||
|
||||
try
|
||||
{
|
||||
var physicalAddress = PhysicalAddress.Parse(macAddress);
|
||||
var addressBytes = physicalAddress.GetAddressBytes();
|
||||
var data = GetWolPacket(addressBytes);
|
||||
var ipAddress = ipAddressString != null ? IPAddress.Parse(ipAddressString) : null;
|
||||
|
||||
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var ni in interfaces)
|
||||
{
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.Name.Contains("VirtualBox") || ni.Name.Contains("vEthernet") || ni.Description.Contains("Virtual"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Logger.Debug($"Found network: {ni.Name} ({ni.Description}), type: {ni.NetworkInterfaceType}");
|
||||
try
|
||||
{
|
||||
if (ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var uip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (uip.Address.ToString().StartsWith("169.254") || uip.Address.AddressFamily != AddressFamily.InterNetwork)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Debug($"Broadcast WOL in network: {ni.Name} ({ni.Description}), local address: {uip.Address}, destination IP-address: {ipAddressString ?? "broadcast"}");
|
||||
|
||||
if (ipAddress != null)
|
||||
{
|
||||
var localEP = new IPEndPoint(uip.Address, 0);
|
||||
|
||||
SendWolToIpAddress(uip.Address, ipAddress, data, physicalAddress);
|
||||
}
|
||||
|
||||
BroadcastWol(uip.Address, IPAddress.Broadcast, data);
|
||||
|
||||
var parts = uip.Address.ToString().Split(".").ToList();
|
||||
parts[3] = "0";
|
||||
var broadcastAddress = IPAddress.Parse(string.Join(".", parts));
|
||||
BroadcastWol(uip.Address, broadcastAddress, data);
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"WakeFunctionToAllNics: while sending to specific network: {ni.Name} ({ni.Description}): {ex.ToLogString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"WakeFunctionToAllNics: {ex.ToLogString()}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
|
||||
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="password"/> is not 0 or 6.</exception>
|
||||
private static byte[] GetWolPacket(byte[] macAddress, byte[] password = null)
|
||||
{
|
||||
if (macAddress == null)
|
||||
throw new ArgumentNullException(nameof(macAddress));
|
||||
if (macAddress.Length != 6)
|
||||
throw new ArgumentException(ArgumentExceptionInvalidMacAddressLength);
|
||||
|
||||
password = password ?? new byte[0];
|
||||
if (password.Length != 0 && password.Length != 6)
|
||||
throw new ArgumentException(ArgumentExceptionInvalidPasswordLength);
|
||||
|
||||
var packet = new byte[17 * 6 + password.Length];
|
||||
|
||||
int offset, i;
|
||||
for (offset = 0; offset < 6; ++offset)
|
||||
packet[offset] = 0xFF;
|
||||
|
||||
for (offset = 6; offset < 17 * 6; offset += 6)
|
||||
for (i = 0; i < 6; ++i)
|
||||
packet[i + offset] = macAddress[i];
|
||||
|
||||
if (password.Length > 0)
|
||||
{
|
||||
for (offset = 16 * 6 + 6; offset < (17 * 6 + password.Length); offset += 6)
|
||||
for (i = 0; i < 6; ++i)
|
||||
packet[i + offset] = password[i];
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static void BroadcastWol(IPAddress localAddress, IPAddress broadCastAddress, byte[] data)
|
||||
{
|
||||
//var localEP = new IPEndPoint(localAddress, 0);
|
||||
var udpc = new UdpClient();
|
||||
|
||||
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
//udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
|
||||
|
||||
var target = new IPEndPoint(broadCastAddress, DefaultWolPort);
|
||||
|
||||
udpc.Send(data, data.Length, target);
|
||||
}
|
||||
|
||||
private static void SendWolToIpAddress(IPAddress localAddress, IPAddress remoteAddress, byte[] data, PhysicalAddress physicalAddress)
|
||||
{
|
||||
//var localEP = new IPEndPoint(localAddress, 0);
|
||||
|
||||
var netLuid = CreateTransientLocalNetEntry(remoteAddress, physicalAddress);
|
||||
try
|
||||
{
|
||||
var udpc = new UdpClient();
|
||||
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
|
||||
var target = new IPEndPoint(remoteAddress, DefaultWolPort);
|
||||
|
||||
udpc.Send(data, data.Length, target);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DeleteNetEntry(netLuid, remoteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private static NET_LUID CreateTransientLocalNetEntry(IPAddress ipAddress, PhysicalAddress macAddress)
|
||||
{
|
||||
var ipBytes = ipAddress.GetAddressBytes();
|
||||
|
||||
var address = new Ws2_32.SOCKADDR_INET
|
||||
{
|
||||
si_family = Ws2_32.ADDRESS_FAMILY.AF_INET,
|
||||
Ipv4 = new Ws2_32.SOCKADDR_IN(new Ws2_32.IN_ADDR(ipBytes), DefaultWolPort),
|
||||
};
|
||||
|
||||
var net = new NET_LUID();
|
||||
var addr = new Ws2_32.SOCKADDR_INET();
|
||||
|
||||
MIB_IPFORWARD_ROW2 forwardRow;
|
||||
var result = GetBestRoute2(net, 0, addr, address, 0, out forwardRow, out _);
|
||||
|
||||
if (result != Win32Error.ERROR_SUCCESS)
|
||||
{
|
||||
Logger.Debug($"Cannot find best route: {result}");
|
||||
|
||||
var luidDummy = new NET_LUID();
|
||||
|
||||
return luidDummy;
|
||||
}
|
||||
var luid = forwardRow.InterfaceLuid;
|
||||
|
||||
var macAddressBytes = macAddress.GetAddressBytes();
|
||||
var bytes = new byte[32];
|
||||
|
||||
macAddressBytes.CopyTo(bytes, 0);
|
||||
|
||||
var row = new MIB_IPNET_ROW2
|
||||
{
|
||||
Address = address,
|
||||
InterfaceLuid = luid,
|
||||
PhysicalAddress = bytes,
|
||||
PhysicalAddressLength = 6
|
||||
};
|
||||
|
||||
result = CreateIpNetEntry2(ref row);
|
||||
|
||||
if (result != Win32Error.ERROR_SUCCESS)
|
||||
{
|
||||
Logger.Debug($"Cannot create ip net entry: {result}");
|
||||
}
|
||||
|
||||
return luid;
|
||||
}
|
||||
|
||||
private static void DeleteNetEntry(NET_LUID luid, IPAddress ipAddress)
|
||||
{
|
||||
if (luid.Value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ipBytes = ipAddress.GetAddressBytes();
|
||||
|
||||
var address = new Ws2_32.SOCKADDR_INET
|
||||
{
|
||||
si_family = Ws2_32.ADDRESS_FAMILY.AF_INET,
|
||||
Ipv4 = new Ws2_32.SOCKADDR_IN(new Ws2_32.IN_ADDR(ipBytes), DefaultWolPort)
|
||||
|
||||
};
|
||||
|
||||
var row = new MIB_IPNET_ROW2
|
||||
{
|
||||
Address = address,
|
||||
InterfaceLuid = luid,
|
||||
};
|
||||
|
||||
var result = DeleteIpNetEntry2(ref row);
|
||||
|
||||
if (result != Win32Error.ERROR_SUCCESS)
|
||||
{
|
||||
Logger.Debug($"Cannot delete ip net entry: {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-9
@@ -1,23 +1,47 @@
|
||||
namespace ColorControl
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class Config
|
||||
public enum ElevationMethod
|
||||
{
|
||||
None = 0,
|
||||
RunAsAdmin = 1,
|
||||
UseService = 2,
|
||||
UseElevatedProcess = 3
|
||||
}
|
||||
|
||||
public class ListViewSortState
|
||||
{
|
||||
public SortOrder SortOrder { get; set; }
|
||||
public int SortIndex { get; set; }
|
||||
}
|
||||
|
||||
public class Config
|
||||
{
|
||||
public bool StartMinimized { get; set; }
|
||||
|
||||
public bool MinimizeOnClose { get; set; }
|
||||
|
||||
public bool MinimizeToTray { get; set; }
|
||||
|
||||
public bool CheckForUpdates { get; set; }
|
||||
public bool AutoInstallUpdates { get; set; }
|
||||
public int DisplaySettingsDelay { get; set; }
|
||||
|
||||
public string ScreenSaverShortcut { get; set; }
|
||||
|
||||
public int FormWidth { get; set; }
|
||||
public int FormHeight { get; set; }
|
||||
|
||||
public int NvPresetId_ApplyOnStartup { get; set; }
|
||||
|
||||
public int AmdPresetId_ApplyOnStartup { get; set; }
|
||||
public bool FixChromeFonts { get; set; }
|
||||
public bool UseGdiScaling { get; set; }
|
||||
public ListViewSortState NvPresetsSortState { get; set; }
|
||||
public ListViewSortState AmdPresetsSortState { get; set; }
|
||||
public ListViewSortState LgPresetsSortState { get; set; }
|
||||
public ListViewSortState GamePresetsSortState { get; set; }
|
||||
public string NvQuickAccessShortcut { get; set; }
|
||||
public string AmdQuickAccessShortcut { get; set; }
|
||||
public string LgQuickAccessShortcut { get; set; }
|
||||
public string GameQuickAccessShortcut { get; set; }
|
||||
public bool UseDedicatedElevatedProcess { get; set; }
|
||||
public ElevationMethod ElevationMethod { get; set; }
|
||||
public bool ElevationMethodAsked { get; set; }
|
||||
|
||||
public Config()
|
||||
{
|
||||
@@ -26,6 +50,16 @@
|
||||
FormWidth = 900;
|
||||
FormHeight = 600;
|
||||
MinimizeToTray = true;
|
||||
CheckForUpdates = true;
|
||||
FixChromeFonts = false;
|
||||
UseGdiScaling = true;
|
||||
UseDedicatedElevatedProcess = false;
|
||||
ElevationMethod = ElevationMethod.None;
|
||||
ElevationMethodAsked = false;
|
||||
LgPresetsSortState = new ListViewSortState();
|
||||
NvPresetsSortState = new ListViewSortState();
|
||||
AmdPresetsSortState = new ListViewSortState();
|
||||
GamePresetsSortState = new ListViewSortState();
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
namespace ColorControl.Forms
|
||||
{
|
||||
partial class ElevatedForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// ElevatedForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(251, 124);
|
||||
this.Name = "ElevatedForm";
|
||||
this.Text = "ElevatedForm";
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using ColorControl.Native;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Forms
|
||||
{
|
||||
public partial class ElevatedForm : Form
|
||||
{
|
||||
public ElevatedForm()
|
||||
{
|
||||
File.Delete("d:\\Msg.txt");
|
||||
InitializeComponent();
|
||||
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
ShowInTaskbar = false;
|
||||
Load += ElevatedForm_Load;
|
||||
Shown += ElevatedForm_Shown;
|
||||
|
||||
var filterStatus = new WinApi.CHANGEFILTERSTRUCT();
|
||||
filterStatus.size = (uint)Marshal.SizeOf(filterStatus);
|
||||
filterStatus.info = 0;
|
||||
|
||||
var result = WinApi.ChangeWindowMessageFilterEx(Handle, NativeConstants.WM_COPYDATA, WinApi.ChangeWindowMessageFilterExAction.Allow, ref filterStatus);
|
||||
//File.AppendAllLines("d:\\Msg.txt", new[] { "ChangeWindowMessageFilterEx result: " + result });
|
||||
|
||||
Task.Run(() => CheckMutexAsync());
|
||||
}
|
||||
|
||||
private void ElevatedForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
protected void ElevatedForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
Size = new Size(0, 0);
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
if (m.Msg == NativeConstants.WM_COPYDATA)
|
||||
{
|
||||
var container = (WinApi.COPYDATASTRUCT)m.GetLParam(typeof(WinApi.COPYDATASTRUCT));
|
||||
var message = Marshal.PtrToStringAnsi(container.lpData);
|
||||
|
||||
//File.AppendAllLines("d:\\Msg.txt", new[] { "Message received: " + message });
|
||||
|
||||
HandleMessage(message);
|
||||
|
||||
//Utils.StartProcess("notepad.exe");
|
||||
|
||||
//BackColor = Color.Red;
|
||||
//Text = "AHA!";
|
||||
//MessageForms.WarningOk("Received!");
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
private void HandleMessage(string message)
|
||||
{
|
||||
var args = message.Split(' ');
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
try
|
||||
{
|
||||
Program.HandleStartupParams(startUpParams, null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckMutexAsync()
|
||||
{
|
||||
var mutex = new Mutex(false, Program.MutexId, out var created);
|
||||
|
||||
File.AppendAllLines("d:\\Msg.txt", new[] { "Mutex created: " + created });
|
||||
|
||||
try
|
||||
{
|
||||
mutex.WaitOne();
|
||||
File.AppendAllLines("d:\\Msg.txt", new[] { "Mutex released?" });
|
||||
}
|
||||
catch (AbandonedMutexException)
|
||||
{
|
||||
File.AppendAllLines("d:\\Msg.txt", new[] { "Mutex abandoned" });
|
||||
}
|
||||
BeginInvoke(() => Application.Exit());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,487 @@
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.Common;
|
||||
using NStandard;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Forms
|
||||
{
|
||||
static class FormUtils
|
||||
{
|
||||
public enum UserNotificationState : int
|
||||
{
|
||||
QUNS_NOT_PRESENT = 1,
|
||||
QUNS_BUSY,
|
||||
QUNS_RUNNING_D3D_FULL_SCREEN,
|
||||
QUNS_PRESENTATION_MODE,
|
||||
QUNS_ACCEPTS_NOTIFICATIONS,
|
||||
QUNS_QUIET_TIME,
|
||||
QUNS_APP
|
||||
}
|
||||
|
||||
public static UserNotificationState[] NotificationsDisabledStates = new[] {
|
||||
UserNotificationState.QUNS_BUSY,
|
||||
UserNotificationState.QUNS_RUNNING_D3D_FULL_SCREEN,
|
||||
UserNotificationState.QUNS_PRESENTATION_MODE,
|
||||
};
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
public static extern int SHQueryUserNotificationState(out UserNotificationState pquns);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
private static UserNotificationState LastNotificationState;
|
||||
|
||||
public static void SetNotifyIconText(NotifyIcon ni, string text)
|
||||
{
|
||||
text = text.Substring(0, Math.Min(text.Length, 127));
|
||||
Type t = typeof(NotifyIcon);
|
||||
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
t.GetField("text", hidden).SetValue(ni, text);
|
||||
if ((bool)t.GetField("added", hidden).GetValue(ni))
|
||||
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
|
||||
}
|
||||
|
||||
public static ToolStripMenuItem BuildDropDownMenuEx(ToolStripItemCollection items, string parentName, string name, Type enumType, EventHandler clickEvent, object tag = null, int min = 0, int max = 0, bool noSubItems = false)
|
||||
{
|
||||
var subMenuName = $"{parentName}_{name}";
|
||||
var subMenuItems = items.Find(subMenuName, false);
|
||||
|
||||
if (subMenuItems.Length > 0)
|
||||
{
|
||||
return subMenuItems[0] as ToolStripMenuItem;
|
||||
}
|
||||
|
||||
ToolStripMenuItem subMenuItem;
|
||||
subMenuItem = (ToolStripMenuItem)items.Add(name);
|
||||
subMenuItem.Name = subMenuName;
|
||||
|
||||
if (noSubItems)
|
||||
{
|
||||
subMenuItem.Tag = tag;
|
||||
subMenuItem.Click += clickEvent;
|
||||
|
||||
return subMenuItem;
|
||||
}
|
||||
|
||||
if (enumType != null)
|
||||
{
|
||||
foreach (var enumValue in Enum.GetValues(enumType))
|
||||
{
|
||||
var itemText = (enumValue as IConvertible).GetDescription() ?? enumValue.ToString();
|
||||
var item = subMenuItem.DropDownItems.Add(itemText);
|
||||
item.Tag = tag;
|
||||
var strValue = enumValue.ToString();
|
||||
if (strValue.EndsWith("_"))
|
||||
{
|
||||
strValue = strValue.Substring(0, strValue.Length - 1);
|
||||
}
|
||||
item.AccessibleName = strValue;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
else if (max > min)
|
||||
{
|
||||
List<int> range;
|
||||
|
||||
if (max - min <= 20)
|
||||
{
|
||||
range = Enumerable.Range(min, (max - min) + 1).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
range = new List<int>();
|
||||
for (var i = min < 0 ? -10 : 0; i <= 10; i++)
|
||||
{
|
||||
range.Add(i * (max / 10));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var value in range)
|
||||
{
|
||||
var subSubItemName = $"{subMenuName}_{value}";
|
||||
|
||||
var item = subMenuItem.DropDownItems.Add(value.ToString());
|
||||
item.Name = subSubItemName;
|
||||
item.Tag = tag;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subMenuItem.Tag = tag;
|
||||
subMenuItem.Click += clickEvent;
|
||||
}
|
||||
|
||||
return subMenuItem;
|
||||
}
|
||||
|
||||
public static void BuildDropDownMenu(ToolStripDropDownItem mnuParent, string name, Type enumType, object colorData, string propertyName, EventHandler clickEvent)
|
||||
{
|
||||
PropertyInfo property = null;
|
||||
var subMenuItems = mnuParent.DropDownItems.Find("miColorSettings_" + name, false);
|
||||
ToolStripMenuItem subMenuItem;
|
||||
if (subMenuItems.Length == 0)
|
||||
{
|
||||
subMenuItem = (ToolStripMenuItem)mnuParent.DropDownItems.Add(name);
|
||||
subMenuItem.Name = "miColorSettings_" + name;
|
||||
|
||||
if (colorData != null)
|
||||
{
|
||||
property = colorData.GetType().GetDeclaredProperty(propertyName);
|
||||
subMenuItem.Tag = property;
|
||||
}
|
||||
|
||||
foreach (var enumValue in Enum.GetValues(enumType))
|
||||
{
|
||||
var item = subMenuItem.DropDownItems.Add(enumValue.ToString());
|
||||
item.Tag = enumValue;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subMenuItem = (ToolStripMenuItem)subMenuItems[0];
|
||||
property = (PropertyInfo)subMenuItem.Tag;
|
||||
}
|
||||
|
||||
if (colorData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var value = property.GetValue(colorData);
|
||||
|
||||
foreach (var item in subMenuItem.DropDownItems)
|
||||
{
|
||||
if (item is ToolStripMenuItem)
|
||||
{
|
||||
var menuItem = (ToolStripMenuItem)item;
|
||||
if (menuItem.Tag != null)
|
||||
{
|
||||
menuItem.Checked = menuItem.Tag.Equals(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BuildComboBox<T>(ComboBox comboBox, params T[] skip) where T : IConvertible
|
||||
{
|
||||
if (comboBox.Items.Count == 0)
|
||||
{
|
||||
foreach (var enumValue in Enum.GetValues(typeof(T)))
|
||||
{
|
||||
if (skip.Contains((T)enumValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = new EnumComboBoxItem
|
||||
{
|
||||
Name = ((T)enumValue).GetDescription(),
|
||||
Id = (int)enumValue
|
||||
};
|
||||
|
||||
comboBox.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int SetComboBoxEnumIndex(ComboBox comboBox, int value)
|
||||
{
|
||||
if (comboBox.Items.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < comboBox.Items.Count; i++)
|
||||
{
|
||||
var item = comboBox.Items[i] as EnumComboBoxItem;
|
||||
if (item.Id == value)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.SelectedIndex = index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public static T GetComboBoxEnumItem<T>(ComboBox comboBox) where T : IConvertible
|
||||
{
|
||||
if (comboBox.SelectedIndex == -1)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), "None");
|
||||
}
|
||||
|
||||
var item = comboBox.SelectedItem as EnumComboBoxItem;
|
||||
|
||||
return (T)Enum.Parse(typeof(T), item.Id.ToString());
|
||||
}
|
||||
|
||||
public static void InitListView(ListView listView, IEnumerable<string> columns)
|
||||
{
|
||||
foreach (var name in columns)
|
||||
{
|
||||
var columnName = name;
|
||||
var parts = name.Split('|');
|
||||
|
||||
var width = 120;
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
width = int.Parse(parts[1]);
|
||||
columnName = parts[0];
|
||||
}
|
||||
|
||||
var header = listView.Columns.Add(columnName);
|
||||
header.Width = width == 120 ? -2 : width;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddOrUpdateListItem<T>(ListView listView, List<T> presets, Config config, T preset = null, ListViewItem specItem = null) where T : PresetBase
|
||||
{
|
||||
ListViewItem item = null;
|
||||
if (preset == null)
|
||||
{
|
||||
item = listView.SelectedItems[0];
|
||||
preset = (T)item.Tag;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = specItem;
|
||||
}
|
||||
|
||||
if (preset.id == 0)
|
||||
{
|
||||
preset.id = preset.GetHashCode();
|
||||
}
|
||||
|
||||
var values = preset.GetDisplayValues(config);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = listView.Items.Add(values[0]);
|
||||
item.Tag = preset;
|
||||
item.Checked = preset.ShowInQuickAccess;
|
||||
for (var i = 1; i < values.Count; i++)
|
||||
{
|
||||
item.SubItems.Add(values[i]);
|
||||
}
|
||||
if (!presets.Any(x => x.id == preset.id))
|
||||
{
|
||||
presets.Add(preset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Text = values[0];
|
||||
item.Checked = preset.ShowInQuickAccess;
|
||||
for (var i = 1; i < values.Count; i++)
|
||||
{
|
||||
if (item.SubItems.Count == i)
|
||||
{
|
||||
item.SubItems.Add(values[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SubItems[i].Text = values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listView.ListViewItemSorter is ListViewColumnSorter sorter && sorter?.Order != SortOrder.None)
|
||||
{
|
||||
listView.Sort();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsForegroundFullScreenAndDisabledNotifications(Screen screen = null)
|
||||
{
|
||||
return IsNotificationDisabled() && IsForegroundFullScreen(screen);
|
||||
}
|
||||
|
||||
public static bool IsNotificationDisabled()
|
||||
{
|
||||
var result = SHQueryUserNotificationState(out var state);
|
||||
|
||||
if (result == NativeConstants.ERROR_SUCCESS && state != LastNotificationState)
|
||||
{
|
||||
Logger.Debug($"Detected NotificationState change from {LastNotificationState} to {state}");
|
||||
LastNotificationState = state;
|
||||
}
|
||||
|
||||
return NotificationsDisabledStates.Contains(state);
|
||||
//return state != UserNotificationState.QUNS_ACCEPTS_NOTIFICATIONS && state != UserNotificationState.QUNS_QUIET_TIME;
|
||||
}
|
||||
|
||||
public static bool IsForegroundFullScreen(Screen screen = null)
|
||||
{
|
||||
if (screen == null)
|
||||
{
|
||||
screen = Screen.PrimaryScreen;
|
||||
}
|
||||
var hWnd = NativeMethods.GetForegroundWindow();
|
||||
|
||||
NativeMethods.GetWindowRect(hWnd, out var rect);
|
||||
|
||||
/* in case you want the process name:
|
||||
uint procId = 0;
|
||||
GetWindowThreadProcessId(hWnd, out procId);
|
||||
var proc = System.Diagnostics.Process.GetProcessById((int)procId);
|
||||
Console.WriteLine(proc.ProcessName);
|
||||
*/
|
||||
|
||||
return screen.Bounds.Width <= (rect.right - rect.left) && screen.Bounds.Height <= (rect.bottom - rect.top);
|
||||
}
|
||||
|
||||
public static (uint, bool) GetForegroundProcessIdAndIfFullScreen(Screen screen = null)
|
||||
{
|
||||
if (screen == null)
|
||||
{
|
||||
screen = Screen.PrimaryScreen;
|
||||
}
|
||||
var hWnd = NativeMethods.GetForegroundWindow();
|
||||
|
||||
if (hWnd == IntPtr.Zero)
|
||||
{
|
||||
return (0, false);
|
||||
}
|
||||
|
||||
NativeMethods.GetWindowRect(hWnd, out var rect);
|
||||
|
||||
GetWindowThreadProcessId(hWnd, out var processId);
|
||||
|
||||
var isFullScreen = screen.Bounds.Width <= (rect.right - rect.left) && screen.Bounds.Height <= (rect.bottom - rect.top);
|
||||
|
||||
return (processId, isFullScreen);
|
||||
}
|
||||
|
||||
public static Process GetForegroundFullScreenProcess(Screen screen = null)
|
||||
{
|
||||
var (processId, isFullScreen) = GetForegroundProcessIdAndIfFullScreen(screen);
|
||||
|
||||
if (!isFullScreen)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Process.GetProcessById((int)processId);
|
||||
}
|
||||
|
||||
public static void EnableControls(Control parent, bool enable = true, IEnumerable<Control> excludedControls = null)
|
||||
{
|
||||
for (var i = 0; i < parent.Controls.Count; i++)
|
||||
{
|
||||
var control = parent.Controls[i];
|
||||
if (!excludedControls?.Contains(control) ?? true)
|
||||
{
|
||||
control.Enabled = enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddStepToTextBox(TextBox textBox, string step)
|
||||
{
|
||||
var text = textBox.Text;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = step;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pos = textBox.SelectionStart;
|
||||
while (pos < text.Length && text.CharAt(pos) != ',')
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
if (pos == text.Length)
|
||||
{
|
||||
text += ", " + step;
|
||||
}
|
||||
else
|
||||
{
|
||||
text = text.Substring(0, pos + 1) + " " + step + ", " + text.Substring(pos + 1).Trim();
|
||||
}
|
||||
}
|
||||
textBox.Text = text.Trim();
|
||||
}
|
||||
|
||||
public static T GetSelectedItemTag<T>(this ListView listView)
|
||||
{
|
||||
if (listView.SelectedItems.Count > 0)
|
||||
{
|
||||
var item = listView.SelectedItems[0];
|
||||
return (T)item.Tag;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FireEvent(this Control ctrl, string eventName, params object[] eventParams)
|
||||
{
|
||||
//EventSelectedIndexChanged
|
||||
var propertyInfo = ctrl.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
||||
var eventHandlerList = propertyInfo.GetValue(ctrl) as EventHandlerList;
|
||||
|
||||
var headField = typeof(EventHandlerList).GetField("_head", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var assembly = typeof(EventHandlerList).Assembly;
|
||||
var listEntryType = assembly.GetTypes().FirstOrDefault(t => t.Name == "ListEntry");
|
||||
|
||||
var nextField = listEntryType.GetField("_next", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var handlerField = listEntryType.GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var entry = headField.GetValue(eventHandlerList);
|
||||
Delegate foundHandler = null;
|
||||
|
||||
while (entry != null)
|
||||
{
|
||||
var handler = handlerField.GetValue(entry) as Delegate;
|
||||
|
||||
if (handler.Method.Name.Contains(eventName))
|
||||
{
|
||||
foundHandler = handler;
|
||||
break;
|
||||
}
|
||||
|
||||
entry = nextField.GetValue(entry);
|
||||
}
|
||||
|
||||
if (foundHandler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var invocationList = foundHandler.GetInvocationList();
|
||||
foreach (var item in invocationList)
|
||||
{
|
||||
item.Method.Invoke(item.Target, eventParams);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EnumComboBoxItem
|
||||
{
|
||||
public string Name;
|
||||
public int Id;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is an implementation of the 'IComparer' interface.
|
||||
/// </summary>
|
||||
public class ListViewColumnSorter : IComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the column to be sorted
|
||||
/// </summary>
|
||||
private int ColumnToSort;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the order in which to sort (i.e. 'Ascending').
|
||||
/// </summary>
|
||||
private SortOrder OrderOfSort;
|
||||
|
||||
/// <summary>
|
||||
/// Case insensitive comparer object
|
||||
/// </summary>
|
||||
private CaseInsensitiveComparer ObjectCompare;
|
||||
|
||||
/// <summary>
|
||||
/// Class constructor. Initializes various elements
|
||||
/// </summary>
|
||||
public ListViewColumnSorter()
|
||||
{
|
||||
// Initialize the column to '0'
|
||||
ColumnToSort = 0;
|
||||
|
||||
// Initialize the sort order to 'none'
|
||||
OrderOfSort = SortOrder.None;
|
||||
|
||||
// Initialize the CaseInsensitiveComparer object
|
||||
ObjectCompare = new CaseInsensitiveComparer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
|
||||
/// </summary>
|
||||
/// <param name="x">First object to be compared</param>
|
||||
/// <param name="y">Second object to be compared</param>
|
||||
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
int compareResult;
|
||||
ListViewItem listviewX, listviewY;
|
||||
|
||||
// Cast the objects to be compared to ListViewItem objects
|
||||
listviewX = (ListViewItem)x;
|
||||
listviewY = (ListViewItem)y;
|
||||
|
||||
if (listviewX.Tag == null)
|
||||
{
|
||||
compareResult = OrderOfSort == SortOrder.Ascending ? -1 : 1;
|
||||
}
|
||||
else if (listviewY.Tag == null)
|
||||
{
|
||||
compareResult = OrderOfSort == SortOrder.Ascending ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compare the two items
|
||||
var subItemTextX = listviewX.SubItems.Count > ColumnToSort ? listviewX.SubItems[ColumnToSort].Text : string.Empty;
|
||||
var subItemTextY = listviewY.SubItems.Count > ColumnToSort ? listviewY.SubItems[ColumnToSort].Text : string.Empty;
|
||||
compareResult = ObjectCompare.Compare(subItemTextX, subItemTextY);
|
||||
}
|
||||
|
||||
// Calculate correct return value based on object comparison
|
||||
if (OrderOfSort == SortOrder.Ascending)
|
||||
{
|
||||
// Ascending sort is selected, return normal result of compare operation
|
||||
return compareResult;
|
||||
}
|
||||
else if (OrderOfSort == SortOrder.Descending)
|
||||
{
|
||||
// Descending sort is selected, return negative result of compare operation
|
||||
return (-compareResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return '0' to indicate they are equal
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
|
||||
/// </summary>
|
||||
public int SortColumn
|
||||
{
|
||||
set
|
||||
{
|
||||
ColumnToSort = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
return ColumnToSort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
|
||||
/// </summary>
|
||||
public SortOrder Order
|
||||
{
|
||||
set
|
||||
{
|
||||
OrderOfSort = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
return OrderOfSort;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using ColorControl.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Forms
|
||||
{
|
||||
class MessageForms
|
||||
{
|
||||
public enum FieldType
|
||||
{
|
||||
Text,
|
||||
Numeric,
|
||||
DropDown,
|
||||
Flags,
|
||||
Shortcut,
|
||||
}
|
||||
|
||||
public class FieldDefinition
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public FieldType FieldType { get; set; } = FieldType.Text;
|
||||
public IEnumerable<string> Values { get; set; }
|
||||
public decimal MinValue { get; set; }
|
||||
public decimal MaxValue { get; set; }
|
||||
public int NumberOfValues { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
public class MessageForm : Form
|
||||
{
|
||||
public void edtShortcut_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
((TextBox)sender).Text = Utils.FormatKeyboardShortcut(e);
|
||||
}
|
||||
|
||||
public void edtShortcut_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
Utils.HandleKeyboardShortcutUp(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Form MainForm;
|
||||
|
||||
public static void WarningOk(string text)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
public static void ErrorOk(string text)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
public static void InfoOk(string text, string title = null, string url = null)
|
||||
{
|
||||
if (url != null)
|
||||
{
|
||||
MessageBox.Show(text, title ?? MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0, url);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(text, title ?? MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
public static DialogResult QuestionYesNo(string text)
|
||||
{
|
||||
return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<string> labels, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
|
||||
{
|
||||
var fieldDefinitions = labels.Select(l => new FieldDefinition
|
||||
{
|
||||
Label = l
|
||||
}).ToList();
|
||||
|
||||
return ShowDialog(caption, fieldDefinitions, validateFunc);
|
||||
}
|
||||
|
||||
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<FieldDefinition> fields, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
|
||||
{
|
||||
var values = new List<FieldDefinition>();
|
||||
|
||||
var prompt = new MessageForm()
|
||||
{
|
||||
Width = 500,
|
||||
Height = 90 + (fields.Count() * 55),
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen
|
||||
};
|
||||
|
||||
var top = 10;
|
||||
var boxes = new List<Control>();
|
||||
var counter = 1;
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var label = field.Label;
|
||||
var textLabel = new Label() { Left = 40, Top = top, Text = label, AutoSize = true };
|
||||
top += 25;
|
||||
prompt.Controls.Add(textLabel);
|
||||
|
||||
Control control;
|
||||
|
||||
switch (field.FieldType)
|
||||
{
|
||||
case FieldType.Text:
|
||||
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (label.Contains("Ip-address"))
|
||||
{
|
||||
//textBox.Mask = "990.990.990.990";
|
||||
//textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
else if (label.Contains("MAC-address"))
|
||||
{
|
||||
textBox.Mask = "AA-AA-AA-AA-AA-AA";
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
control = textBox;
|
||||
break;
|
||||
|
||||
case FieldType.Shortcut:
|
||||
var shortcutTextBox = new TextBox()
|
||||
{
|
||||
Left = 40,
|
||||
Top = top,
|
||||
Width = 200,
|
||||
Name = $"edtShortcut_{counter}",
|
||||
ReadOnly = true
|
||||
};
|
||||
|
||||
shortcutTextBox.KeyDown += prompt.edtShortcut_KeyDown;
|
||||
shortcutTextBox.KeyUp += prompt.edtShortcut_KeyUp;
|
||||
|
||||
if (field.Value != null)
|
||||
{
|
||||
shortcutTextBox.Text = field.Value.ToString();
|
||||
}
|
||||
|
||||
control = shortcutTextBox;
|
||||
break;
|
||||
|
||||
case FieldType.Numeric:
|
||||
var numericEdit = new NumericUpDown() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (field.MinValue != field.MaxValue)
|
||||
{
|
||||
numericEdit.Minimum = field.MinValue;
|
||||
numericEdit.Maximum = field.MaxValue;
|
||||
}
|
||||
|
||||
if (field.Value != null)
|
||||
{
|
||||
numericEdit.Text = field.Value.ToString();
|
||||
}
|
||||
|
||||
control = numericEdit;
|
||||
break;
|
||||
|
||||
case FieldType.DropDown:
|
||||
var comboBox = new ComboBox
|
||||
{
|
||||
Left = 40,
|
||||
Top = top,
|
||||
Width = 400,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
|
||||
if (field.Values != null && field.Values.Any())
|
||||
{
|
||||
comboBox.Items.AddRange(field.Values.ToArray());
|
||||
comboBox.SelectedIndex = field.Value is string strValue ? comboBox.Items.IndexOf(strValue) : 0;
|
||||
}
|
||||
|
||||
control = comboBox;
|
||||
break;
|
||||
|
||||
case FieldType.Flags:
|
||||
var checkedListBox = new CheckedListBox
|
||||
{
|
||||
Left = 40,
|
||||
Top = top,
|
||||
Width = 400,
|
||||
Height = 100,
|
||||
MultiColumn = true
|
||||
};
|
||||
|
||||
top += 80;
|
||||
prompt.Height += 80;
|
||||
|
||||
if (field.Values != null && field.Values.Any())
|
||||
{
|
||||
var compoundValue = field.Value is uint ? (int)(uint)field.Value : (int)field.Value;
|
||||
var enumValue = 1;
|
||||
foreach (var value in field.Values)
|
||||
{
|
||||
var isChecked = (compoundValue & enumValue) == enumValue;
|
||||
|
||||
checkedListBox.Items.Add(value, isChecked);
|
||||
|
||||
enumValue *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
control = checkedListBox;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
top += 30;
|
||||
prompt.Controls.Add(control);
|
||||
boxes.Add(control);
|
||||
control.Tag = field;
|
||||
counter++;
|
||||
}
|
||||
|
||||
var confirmation = new Button() { Text = "OK", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 };
|
||||
confirmation.Click += (sender, e) =>
|
||||
{
|
||||
foreach (var box in boxes)
|
||||
{
|
||||
var field = (FieldDefinition)box.Tag;
|
||||
|
||||
switch (field.FieldType)
|
||||
{
|
||||
case FieldType.Flags:
|
||||
var checkedListBox = (CheckedListBox)box;
|
||||
|
||||
var compoundValue = 0;
|
||||
var enumValue = 1;
|
||||
for (var i = 0; i < checkedListBox.Items.Count; i++)
|
||||
{
|
||||
compoundValue += checkedListBox.GetItemChecked(i) ? enumValue : 0;
|
||||
enumValue *= 2;
|
||||
}
|
||||
|
||||
field.Value = compoundValue;
|
||||
break;
|
||||
default:
|
||||
field.Value = box.Text.Trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (validateFunc != null)
|
||||
{
|
||||
var message = validateFunc(fields);
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
WarningOk(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
values.AddRange(fields);
|
||||
|
||||
prompt.Close();
|
||||
};
|
||||
prompt.Controls.Add(confirmation);
|
||||
prompt.AcceptButton = confirmation;
|
||||
|
||||
prompt.ShowDialog(MainForm);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static Form ShowProgress(string caption)
|
||||
{
|
||||
var prompt = new Form()
|
||||
{
|
||||
Width = 300,
|
||||
Height = 100,
|
||||
FormBorderStyle = FormBorderStyle.FixedToolWindow,
|
||||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen,
|
||||
ControlBox = false
|
||||
};
|
||||
var textLabel = new Label() { Left = 20, Top = 25, Text = caption, AutoSize = true };
|
||||
prompt.Controls.Add(textLabel);
|
||||
|
||||
prompt.Show(MainForm);
|
||||
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
@@ -19,7 +14,6 @@ namespace ColorControl
|
||||
|
||||
public static TreeNode CreateTree(object obj, string text)
|
||||
{
|
||||
JavaScriptSerializer jss = new JavaScriptSerializer();
|
||||
var serialized = JsonConvert.SerializeObject(obj, Formatting.None,
|
||||
new JsonSerializerSettings {
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
@@ -30,7 +24,7 @@ namespace ColorControl
|
||||
},
|
||||
Converters = new [] { new Newtonsoft.Json.Converters.StringEnumConverter() }
|
||||
});
|
||||
Dictionary<string, object> dic = jss.Deserialize<Dictionary<string, object>>(serialized);
|
||||
var dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(serialized);
|
||||
var root = new TreeNode();
|
||||
root.Text = text;
|
||||
BuildTree(dic, root);
|
||||
@@ -48,9 +42,9 @@ namespace ColorControl
|
||||
node.Nodes.Add(keyValueNode);
|
||||
BuildTree(kv.Value, keyValueNode);
|
||||
}
|
||||
else if (item is ArrayList)
|
||||
else if (item.GetType() == typeof(JArray))
|
||||
{
|
||||
ArrayList list = (ArrayList)item;
|
||||
var list = (JArray)item;
|
||||
int index = 0;
|
||||
foreach (object value in list)
|
||||
{
|
||||
@@ -70,6 +64,14 @@ namespace ColorControl
|
||||
BuildTree(d, node);
|
||||
}
|
||||
}
|
||||
else if (item.GetType() == typeof(JObject))
|
||||
{
|
||||
var jobject = item as JObject;
|
||||
foreach (var property in jobject.Properties())
|
||||
{
|
||||
BuildTree(new KeyValuePair<string, object>(property.Name, property.Value), node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetValueAsString(object value)
|
||||
@@ -82,13 +84,13 @@ namespace ColorControl
|
||||
return "[]";
|
||||
}
|
||||
|
||||
if (value is ArrayList)
|
||||
if (value.GetType() == typeof(JArray))
|
||||
{
|
||||
var arr = value as ArrayList;
|
||||
var arr = value as JArray;
|
||||
return $"[{arr.Count}]";
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
if (type.IsGenericType || value.GetType() == typeof(JObject))
|
||||
{
|
||||
return "{}";
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using LgTv;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class LgPreset : PresetBase
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string appId { get; set; }
|
||||
public List<string> steps { get; set; }
|
||||
|
||||
public static List<LgApp> LgApps { get; set; }
|
||||
|
||||
public LgPreset()
|
||||
{
|
||||
steps = new List<string>();
|
||||
}
|
||||
|
||||
public LgPreset(LgPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
name = preset.name + " (copy)";
|
||||
appId = preset.appId;
|
||||
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
steps.Add(step);
|
||||
}
|
||||
}
|
||||
|
||||
public LgPreset Clone()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static List<string> GetColumnNames()
|
||||
{
|
||||
return new List<string>() { "Name", "App|200", "Steps|400", "Shortcut" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
var app = appId;
|
||||
if (LgApps != null)
|
||||
{
|
||||
var item = LgApps.FirstOrDefault(x => x.appId.Equals(appId));
|
||||
if (item != null)
|
||||
{
|
||||
app = item.title + " (" + appId + ")";
|
||||
}
|
||||
}
|
||||
values.Add(app);
|
||||
values.Add(GetStepsDisplay());
|
||||
values.Add(shortcut);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public string GetStepsDisplay()
|
||||
{
|
||||
return steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,770 +0,0 @@
|
||||
using LgTv;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class LgService
|
||||
{
|
||||
public static string LgListAppsJson = "listApps.json";
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
//public string FriendlyScreenName { get; private set; }
|
||||
|
||||
public List<PnpDev> Devices { get; private set; }
|
||||
public PnpDev SelectedDevice
|
||||
{
|
||||
get { return _selectedDevice; }
|
||||
set
|
||||
{
|
||||
_selectedDevice = value;
|
||||
Config.PreferredMacAddress = _selectedDevice != null ? _selectedDevice.MacAddress : null;
|
||||
}
|
||||
}
|
||||
|
||||
public LgServiceConfig Config { get; private set; }
|
||||
|
||||
private List<LgPreset> _lgPresets;
|
||||
private string _lgPresetsFilename;
|
||||
|
||||
private LgTvApi _lgTvApi;
|
||||
private string _dataDir;
|
||||
private string _configFilename;
|
||||
private bool _allowPowerOn;
|
||||
private bool _justWokeUp;
|
||||
private PnpDev _selectedDevice;
|
||||
private string _rcButtonsFilename;
|
||||
private List<LgPreset> _remoteControlButtons;
|
||||
private List<LgApp> _lgApps = new List<LgApp>();
|
||||
|
||||
private JavaScriptSerializer _JsonSerializer = new JavaScriptSerializer();
|
||||
private JavaScriptSerializer _JsonDeserializer = new JavaScriptSerializer();
|
||||
|
||||
private Dictionary<string, Func<bool>> _invokableActions = new Dictionary<string, Func<bool>>();
|
||||
|
||||
private bool _poweredOffByScreenSaver;
|
||||
private int _poweredOffByScreenSaverProcessId;
|
||||
private Task _monitorTask;
|
||||
private int _monitorTaskCounter;
|
||||
|
||||
public LgService(string dataDir, bool allowPowerOn)
|
||||
{
|
||||
_dataDir = dataDir;
|
||||
_allowPowerOn = allowPowerOn;
|
||||
|
||||
_invokableActions.Add("WOL", new Func<bool>(WakeSelectedDevice));
|
||||
|
||||
LgPreset.LgApps = _lgApps;
|
||||
|
||||
LoadConfig();
|
||||
LoadPresets();
|
||||
LoadRemoteControlButtons();
|
||||
|
||||
//foreach (var screen in Screen.AllScreens)
|
||||
//{
|
||||
// var name = screen.DeviceFriendlyName();
|
||||
// if (name.Contains("LG"))
|
||||
// {
|
||||
// FriendlyScreenName = name;
|
||||
// }
|
||||
//}
|
||||
|
||||
var _ = RefreshDevices(afterStartUp: true);
|
||||
}
|
||||
|
||||
~LgService()
|
||||
{
|
||||
GlobalSave();
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SaveConfig();
|
||||
SavePresets();
|
||||
SaveRemoteControlButtons();
|
||||
}
|
||||
|
||||
public List<LgPreset> GetPresets()
|
||||
{
|
||||
return _lgPresets;
|
||||
}
|
||||
|
||||
public LgPreset CreateNewPreset()
|
||||
{
|
||||
var preset = new LgPreset();
|
||||
var name = "New preset";
|
||||
string fullname;
|
||||
var number = 1;
|
||||
do
|
||||
{
|
||||
fullname = $"{name} ({number})";
|
||||
number++;
|
||||
} while (_lgPresets.Any(x => x.name.Equals(fullname)));
|
||||
|
||||
preset.name = fullname;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
{
|
||||
_lgPresetsFilename = Path.Combine(_dataDir, "LgPresets.json");
|
||||
var toCopy = Path.Combine(Directory.GetCurrentDirectory(), "LgPresets.json");
|
||||
if (!File.Exists(_lgPresetsFilename) && File.Exists(toCopy))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(toCopy, _lgPresetsFilename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Error while copying {toCopy} to {_lgPresetsFilename}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(_lgPresetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_lgPresetsFilename);
|
||||
|
||||
_lgPresets = _JsonDeserializer.Deserialize<List<LgPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_lgPresets = new List<LgPreset>();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
{
|
||||
_configFilename = Path.Combine(_dataDir, "LgConfig.json");
|
||||
if (File.Exists(_configFilename))
|
||||
{
|
||||
Config = JsonConvert.DeserializeObject<LgServiceConfig>(File.ReadAllText(_configFilename));
|
||||
}
|
||||
else
|
||||
{
|
||||
Config = new LgServiceConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadRemoteControlButtons()
|
||||
{
|
||||
var defaultButtons = GenerateDefaultRemoteControlButtons();
|
||||
|
||||
_rcButtonsFilename = Path.Combine(_dataDir, "LgRemoteControlButtons.json");
|
||||
//if (File.Exists(_rcButtonsFilename))
|
||||
//{
|
||||
// var json = File.ReadAllText(_rcButtonsFilename);
|
||||
|
||||
// _remoteControlButtons = _JsonDeserializer.Deserialize<List<LgPreset>>(json);
|
||||
|
||||
// var missingButtons = defaultButtons.Where(b => !_remoteControlButtons.Any(x => x.name.Equals(b.name)));
|
||||
// _remoteControlButtons.AddRange(missingButtons);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
_remoteControlButtons = defaultButtons;
|
||||
}
|
||||
}
|
||||
|
||||
public List<LgPreset> GetRemoteControlButtons()
|
||||
{
|
||||
return _remoteControlButtons;
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<bool>> GetInvokableActions()
|
||||
{
|
||||
return _invokableActions;
|
||||
}
|
||||
|
||||
private List<LgPreset> GenerateDefaultRemoteControlButtons()
|
||||
{
|
||||
var list = new List<LgPreset>();
|
||||
|
||||
for (var i = 1; i < 11; i++)
|
||||
{
|
||||
var button = i < 10 ? i : 0;
|
||||
list.Add(GeneratePreset(button.ToString()));
|
||||
}
|
||||
|
||||
list.Add(GeneratePreset("Settings", appId: "com.palm.app.settings", key: Keys.S));
|
||||
|
||||
list.Add(GeneratePreset("Vol +", step: "VOLUMEUP", key: Keys.VolumeUp));
|
||||
list.Add(GeneratePreset("Vol -", step: "VOLUMEDOWN", key: Keys.VolumeDown));
|
||||
list.Add(GeneratePreset("Mute", step: "MUTE", key: Keys.VolumeMute));
|
||||
|
||||
list.Add(GeneratePreset("Up", step: "UP", key: Keys.Up));
|
||||
list.Add(GeneratePreset("Down", step: "DOWN", key: Keys.Down));
|
||||
list.Add(GeneratePreset("Left", step: "LEFT", key: Keys.Left));
|
||||
list.Add(GeneratePreset("Right", step: "RIGHT", key: Keys.Right));
|
||||
|
||||
list.Add(GeneratePreset("Enter", step: "ENTER", key: Keys.Enter));
|
||||
list.Add(GeneratePreset("Back", step: "BACK", key: Keys.Back));
|
||||
list.Add(GeneratePreset("Exit", step: "EXIT", key: Keys.Escape));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private LgPreset GeneratePreset(string name, string step = null, string appId = null, Keys key = Keys.None)
|
||||
{
|
||||
var preset = new LgPreset();
|
||||
|
||||
var cvt = new KeysConverter();
|
||||
var shortcut = key != Keys.None ? (string)cvt.ConvertTo(key, typeof(string)) : null;
|
||||
|
||||
preset.name = name;
|
||||
preset.appId = appId;
|
||||
preset.shortcut = shortcut;
|
||||
if (appId == null)
|
||||
{
|
||||
preset.steps.Add(step == null ? name : step);
|
||||
}
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = _JsonSerializer.Serialize(_lgPresets);
|
||||
File.WriteAllText(_lgPresetsFilename, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e.ToLogString());
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(Config);
|
||||
File.WriteAllText(_configFilename, json);
|
||||
}
|
||||
|
||||
private void SaveRemoteControlButtons()
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(_remoteControlButtons);
|
||||
File.WriteAllText(_rcButtonsFilename, json);
|
||||
}
|
||||
|
||||
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
|
||||
{
|
||||
var devices = await Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
SetDevices(devices);
|
||||
|
||||
if (afterStartUp && SelectedDevice == null && Config.PowerOnAfterStartup && !string.IsNullOrEmpty(Config.PreferredMacAddress))
|
||||
{
|
||||
Logger.Debug("No device has been found, trying to wake it first...");
|
||||
|
||||
WakeSelectedDevice(Config.PreferredMacAddress);
|
||||
|
||||
await Task.Delay(4000);
|
||||
await RefreshDevices();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (connect && SelectedDevice != null)
|
||||
{
|
||||
if (Config.PowerOnAfterStartup && _allowPowerOn)
|
||||
{
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
}
|
||||
else
|
||||
{
|
||||
var _ = ConnectToSelectedDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDevices(List<PnpDev> devices)
|
||||
{
|
||||
Devices = devices;
|
||||
if (Devices?.Count > 0)
|
||||
{
|
||||
var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0];
|
||||
|
||||
SelectedDevice = preferredDevice;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectToSelectedDevice(int retries = 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisposeConnection();
|
||||
_lgTvApi = await LgTvApi.CreateLgTvApi(SelectedDevice.IpAddress, retries);
|
||||
|
||||
//_lgTvApi.GetServiceList();
|
||||
return _lgTvApi != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error($"Error while connecting to {SelectedDevice.IpAddress}: {logMessage}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> Connected(bool reconnect = false)
|
||||
{
|
||||
if (SelectedDevice == null)
|
||||
{
|
||||
Logger.Debug("Cannot apply LG-preset: no device has been selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), SelectedDevice.IpAddress))
|
||||
{
|
||||
if (!await ConnectToSelectedDevice())
|
||||
{
|
||||
Logger.Debug("Cannot apply LG-preset: no connection could be made");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsConnected()
|
||||
{
|
||||
return !_lgTvApi?.ConnectionClosed ?? false;
|
||||
}
|
||||
|
||||
public async Task<bool> ApplyPreset(LgPreset preset, bool reconnect = false)
|
||||
{
|
||||
var hasApp = !string.IsNullOrEmpty(preset.appId);
|
||||
|
||||
var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasWOL)
|
||||
{
|
||||
var connected = await WakeAndConnectToSelectedDevice(0);
|
||||
if (!connected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var tries = 0; tries <= 1; tries++)
|
||||
{
|
||||
if (!await Connected(reconnect || tries == 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasApp)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _lgTvApi.LaunchApp(preset.appId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while launching app: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_justWokeUp)
|
||||
{
|
||||
_justWokeUp = false;
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.steps.Any())
|
||||
{
|
||||
if (hasApp)
|
||||
{
|
||||
await Task.Delay(1500);
|
||||
}
|
||||
try
|
||||
{
|
||||
await ExecuteSteps(_lgTvApi, preset);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error("Error while executing steps: " + logMessage);
|
||||
|
||||
if (tries == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task PowerOn()
|
||||
{
|
||||
var mouse = await _lgTvApi.GetMouse();
|
||||
mouse.SendButton(ButtonType.POWER);
|
||||
}
|
||||
|
||||
private async Task ExecuteSteps(LgTvApi api, LgPreset preset)
|
||||
{
|
||||
var mouse = await api.GetMouse();
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
var key = keySpec[0].ToUpper();
|
||||
if (_invokableActions.ContainsKey(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
SendKey(mouse, key, int.Parse(keySpec[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendKey(mouse, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180)
|
||||
{
|
||||
if (key.Length == 1 && int.TryParse(key, out _))
|
||||
{
|
||||
key = "_" + key;
|
||||
}
|
||||
var button = (ButtonType)Enum.Parse(typeof(ButtonType), key);
|
||||
mouse.SendButton(button);
|
||||
Thread.Sleep(delay);
|
||||
}
|
||||
|
||||
public async Task<LgWebOsMouseService> GetMouseAsync()
|
||||
{
|
||||
return await _lgTvApi.GetMouse();
|
||||
}
|
||||
|
||||
public async Task RefreshApps(bool force = false)
|
||||
{
|
||||
if (SelectedDevice == null)
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no device has been selected");
|
||||
}
|
||||
|
||||
if (!await Connected(force))
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no connection could be made");
|
||||
}
|
||||
|
||||
_lgApps.Clear();
|
||||
_lgApps.AddRange(await _lgTvApi.GetApps(force));
|
||||
}
|
||||
|
||||
public List<LgApp> GetApps()
|
||||
{
|
||||
return _lgApps;
|
||||
}
|
||||
|
||||
internal void DisposeConnection()
|
||||
{
|
||||
if (_lgTvApi != null)
|
||||
{
|
||||
_lgTvApi.Dispose();
|
||||
_lgTvApi = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
{
|
||||
if (!await Connected(true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await _lgTvApi.TurnOff();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice()
|
||||
{
|
||||
return WakeSelectedDevice(SelectedDevice?.MacAddress ?? Config.PreferredMacAddress);
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice(string macAddress)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
macAddress = string.IsNullOrEmpty(macAddress) ? SelectedDevice?.MacAddress : macAddress;
|
||||
if (macAddress != null)
|
||||
{
|
||||
result = WOL.WakeFunction(macAddress, Config.UseOldNpcapWol);
|
||||
_justWokeUp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("Cannot wake device: no device has been selected or there is no preferred MAC-address stored in the configuration");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void WakeAfterResume()
|
||||
{
|
||||
if (Config.PowerOnAfterResume)
|
||||
{
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> WakeAndConnectToSelectedDeviceWithRetries(bool checkUserSession = true)
|
||||
{
|
||||
if (checkUserSession && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: not waking because session info indicates no local session");
|
||||
return false;
|
||||
}
|
||||
|
||||
var wakeDelay = 0;
|
||||
var maxRetries = Config.PowerOnRetries <= 1 ? 5 : Config.PowerOnRetries;
|
||||
|
||||
var result = false;
|
||||
for (var retry = 0; retry < maxRetries && !result; retry++)
|
||||
{
|
||||
Logger.Debug($"WakeAndConnectToSelectedDeviceWithRetries: attempt {retry + 1} of {maxRetries}...");
|
||||
|
||||
var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
result = await WakeAndConnectToSelectedDevice(retry == 0 ? wakeDelay : 0);
|
||||
ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms;
|
||||
if (!result)
|
||||
{
|
||||
var delay = 2000 - ms;
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay((int)delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> WakeAndConnectToSelectedDevice(int wakeDelay = 5000, int connectDelay = 500)
|
||||
{
|
||||
try
|
||||
{
|
||||
//if (wakeDelay == 0)
|
||||
//{
|
||||
// if (await ConnectToSelectedDevice())
|
||||
// {
|
||||
// Logger.Debug("Already connected, no wake needed?");
|
||||
// return;
|
||||
// };
|
||||
//}
|
||||
|
||||
await Task.Delay(wakeDelay);
|
||||
var result = WakeSelectedDevice();
|
||||
if (!result)
|
||||
{
|
||||
Logger.Debug("WOL failed");
|
||||
return false;
|
||||
}
|
||||
Logger.Debug("WOL succeeded");
|
||||
await Task.Delay(connectDelay);
|
||||
result = await ConnectToSelectedDevice();
|
||||
Logger.Debug("Connect succeeded: " + result);
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void InstallEventHandlers()
|
||||
{
|
||||
SystemEvents.PowerModeChanged -= PowerModeChanged;
|
||||
SystemEvents.PowerModeChanged += PowerModeChanged;
|
||||
//SystemEvents.SessionEnded -= SessionEnded;
|
||||
//SystemEvents.SessionEnded += SessionEnded;
|
||||
|
||||
UserSessionInfo.UserSessionSwitch -= SessionSwitched;
|
||||
UserSessionInfo.UserSessionSwitch += SessionSwitched;
|
||||
|
||||
MonitorProcesses();
|
||||
}
|
||||
|
||||
private void SessionSwitched(bool toLocal)
|
||||
{
|
||||
if (toLocal)
|
||||
{
|
||||
if (_poweredOffByScreenSaver)
|
||||
{
|
||||
Logger.Debug("User switched to local and screen was powered off due to screensaver: waking up");
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("User switched to local but screen was not powered off by screensaver: NOT waking up");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
var powerOn = e.Mode == PowerModes.Resume;
|
||||
|
||||
Logger.Debug($"PowerModeChanged: {e.Mode}");
|
||||
|
||||
if (powerOn)
|
||||
{
|
||||
DisposeConnection();
|
||||
WakeAfterResume();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config.PowerOffOnStandby)
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
|
||||
try
|
||||
{
|
||||
Logger.Debug("Powering off tv...");
|
||||
var task = PowerOff();
|
||||
Utils.WaitForTask(task);
|
||||
Logger.Debug("Done powering off tv");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SessionEnded(object sender, SessionEndedEventArgs e)
|
||||
{
|
||||
//if (e.Reason == SessionEndReasons.SystemShutdown)
|
||||
//{
|
||||
// Logger.Debug($"SessionEnded: {e.Reason}");
|
||||
|
||||
// if (Config.PowerOffOnShutdown)
|
||||
// {
|
||||
// PowerOff();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private void MonitorProcesses()
|
||||
{
|
||||
if (Config.PowerSwitchOnScreenSaver && _monitorTask == null)
|
||||
{
|
||||
_monitorTask = CheckProcesses();
|
||||
}
|
||||
else if (!Config.PowerSwitchOnScreenSaver && _monitorTask != null)
|
||||
{
|
||||
_monitorTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckProcesses()
|
||||
{
|
||||
var wasConnected = IsConnected();
|
||||
_monitorTaskCounter++;
|
||||
var validCounter = _monitorTaskCounter;
|
||||
while (Config.PowerSwitchOnScreenSaver && validCounter == _monitorTaskCounter)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
if (_poweredOffByScreenSaver && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var processes = Process.GetProcesses();
|
||||
|
||||
if (_poweredOffByScreenSaver)
|
||||
{
|
||||
if (processes.Any(p => p.Id == _poweredOffByScreenSaverProcessId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Debug("Screensaver stopped, waking");
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
var _ = WakeAndConnectToSelectedDeviceWithRetries();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsConnected())
|
||||
{
|
||||
if (wasConnected)
|
||||
{
|
||||
Logger.Debug("TV was connected, but not any longer");
|
||||
wasConnected = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!wasConnected)
|
||||
{
|
||||
Logger.Debug("TV was not connected, but connection has now been established");
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
foreach (var process in processes)
|
||||
{
|
||||
var name = process.ProcessName.ToLowerInvariant();
|
||||
if (name.EndsWith(".scr"))
|
||||
{
|
||||
var parent = process.Parent();
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
|
||||
try
|
||||
{
|
||||
Logger.Debug("Powering off tv because of screensaver");
|
||||
_poweredOffByScreenSaver = await PowerOff();
|
||||
_poweredOffByScreenSaverProcessId = process.Id;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("CheckProcesses: can't power off: " + e.ToLogString());
|
||||
}
|
||||
if (!IsConnected())
|
||||
{
|
||||
_poweredOffByScreenSaver = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace ColorControl
|
||||
{
|
||||
class LgServiceConfig
|
||||
{
|
||||
public bool PowerOnAfterStartup { get; set; }
|
||||
|
||||
public bool PowerOnAfterResume { get; set; }
|
||||
|
||||
public bool PowerOffOnShutdown { get; set; }
|
||||
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
|
||||
public int PowerOnDelayAfterResume { get; set; }
|
||||
|
||||
public int PowerOnRetries { get; set; }
|
||||
|
||||
public string PreferredMacAddress { get; set; }
|
||||
|
||||
public string DeviceSearchKey { get; set; }
|
||||
|
||||
public bool UseOldNpcapWol { get; set; }
|
||||
|
||||
public LgServiceConfig()
|
||||
{
|
||||
PowerOnDelayAfterResume = 5000;
|
||||
DeviceSearchKey = "[LG]";
|
||||
PowerOnRetries = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1984
-538
File diff suppressed because it is too large.
Load diff
+2633
-332
File diff suppressed because it is too large.
Load diff
+16
-61
@@ -1,64 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
@@ -123,9 +63,24 @@
|
||||
<metadata name="mnuAmdPresets.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>278, 17</value>
|
||||
</metadata>
|
||||
<metadata name="mnuLgExpert.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>583, 17</value>
|
||||
</metadata>
|
||||
<metadata name="mnuLgButtons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>147, 17</value>
|
||||
</metadata>
|
||||
<metadata name="mnuGameOptions.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>705, 16</value>
|
||||
</metadata>
|
||||
<metadata name="mnuGameActions.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>414, 15</value>
|
||||
</metadata>
|
||||
<metadata name="mnuGameAddStep.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>416, 50</value>
|
||||
</metadata>
|
||||
<metadata name="mnuNvSettings.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>15, 47</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class MessageForms
|
||||
{
|
||||
public static Form MainForm;
|
||||
|
||||
public static void WarningOk(string text)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
public static void ErrorOk(string text)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
public static void InfoOk(string text)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
public static DialogResult QuestionYesNo(string text)
|
||||
{
|
||||
return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
// This class takes care of wrapping "Connecting and Configuring Displays(CCD) Win32 API"
|
||||
internal static class CCD
|
||||
{
|
||||
public enum DisplayTopology
|
||||
{
|
||||
Internal,
|
||||
External,
|
||||
Extend,
|
||||
Clone
|
||||
}
|
||||
|
||||
public static DisplayTopology GetDisplayTopology()
|
||||
{
|
||||
uint numPathArrayElements;
|
||||
uint numModeInfoArrayElements;
|
||||
|
||||
NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.DatabaseCurrent, out numPathArrayElements, out numModeInfoArrayElements);
|
||||
|
||||
var pathArray = new DisplayConfigPathInfo[numPathArrayElements];
|
||||
var modeArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
|
||||
|
||||
DisplayConfigTopologyId displayTopology;
|
||||
|
||||
NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref numPathArrayElements, pathArray, ref numModeInfoArrayElements, modeArray, out displayTopology);
|
||||
|
||||
switch (displayTopology)
|
||||
{
|
||||
case DisplayConfigTopologyId.External: return DisplayTopology.External;
|
||||
case DisplayConfigTopologyId.Internal: return DisplayTopology.Internal;
|
||||
case DisplayConfigTopologyId.Extend: return DisplayTopology.Extend;
|
||||
}
|
||||
|
||||
return DisplayTopology.Clone;
|
||||
}
|
||||
|
||||
public static void SetDisplayTopology(DisplayTopology displayTopology)
|
||||
{
|
||||
switch (displayTopology)
|
||||
{
|
||||
case DisplayTopology.External:
|
||||
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExternal));
|
||||
break;
|
||||
case DisplayTopology.Internal:
|
||||
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyInternal));
|
||||
break;
|
||||
case DisplayTopology.Extend:
|
||||
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExtend));
|
||||
break;
|
||||
case DisplayTopology.Clone:
|
||||
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyClone));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetHDRState(bool enabled, string displayName = null)
|
||||
{
|
||||
uint pathCount, modeCount;
|
||||
|
||||
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
|
||||
if (err == NativeConstants.ERROR_SUCCESS)
|
||||
{
|
||||
var pathsArray = new DisplayConfigPathInfo[pathCount];
|
||||
var modesArray = new DisplayConfigModeInfo[modeCount];
|
||||
|
||||
DisplayConfigTopologyId displayTopology;
|
||||
|
||||
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
|
||||
if (err == NativeConstants.ERROR_SUCCESS)
|
||||
{
|
||||
foreach (var path in pathsArray)
|
||||
{
|
||||
// get display name
|
||||
var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
|
||||
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
|
||||
info.header.size = Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
|
||||
info.header.adapterId = path.sourceInfo.adapterId;
|
||||
info.header.id = path.sourceInfo.id;
|
||||
|
||||
err = NativeMethods.DisplayConfigGetDeviceInfo(ref info);
|
||||
if (err != NativeConstants.ERROR_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var deviceName = info.viewGdiDeviceName;
|
||||
if (displayName != null && !EqualDisplayNames(deviceName, displayName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
|
||||
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
|
||||
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
|
||||
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>(); ;
|
||||
|
||||
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
|
||||
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
|
||||
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
|
||||
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>(); ;
|
||||
|
||||
for (int i = 0; i < modeCount; i++)
|
||||
{
|
||||
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
|
||||
{
|
||||
setpacket.header.adapterId = modesArray[i].adapterId;
|
||||
setpacket.header.id = modesArray[i].id;
|
||||
requestpacket.header.adapterId = modesArray[i].adapterId;
|
||||
requestpacket.header.id = modesArray[i].id;
|
||||
|
||||
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS)
|
||||
{
|
||||
if (requestpacket.advancedColorSupported)
|
||||
{
|
||||
setpacket.enableAdvancedColor = enabled ? 1U : 0;
|
||||
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err != NativeConstants.ERROR_SUCCESS)
|
||||
{
|
||||
throw new Win32Exception(err);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool EqualDisplayNames(string displayName1, string displayName2)
|
||||
{
|
||||
displayName1 = displayName1.Replace("\\", string.Empty);
|
||||
displayName2 = displayName2.Replace("\\", string.Empty);
|
||||
|
||||
return displayName1.Equals(displayName2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private enum DISPLAYCONFIG_MODE_INFO_TYPE
|
||||
{
|
||||
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
|
||||
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
|
||||
DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
|
||||
}
|
||||
|
||||
private enum DISPLAYCONFIG_DEVICE_INFO_TYPE
|
||||
{
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
|
||||
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
|
||||
{
|
||||
public DISPLAYCONFIG_DEVICE_INFO_TYPE type;
|
||||
public int size;
|
||||
public LUID adapterId;
|
||||
public uint id;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct LUID
|
||||
{
|
||||
uint LowPart;
|
||||
uint HighPart;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigVideoOutputTechnology : uint
|
||||
{
|
||||
Other = 4294967295, // -1
|
||||
Hd15 = 0,
|
||||
Svideo = 1,
|
||||
CompositeVideo = 2,
|
||||
ComponentVideo = 3,
|
||||
Dvi = 4,
|
||||
Hdmi = 5,
|
||||
Lvds = 6,
|
||||
DJpn = 8,
|
||||
Sdi = 9,
|
||||
DisplayportExternal = 10,
|
||||
DisplayportEmbedded = 11,
|
||||
UdiExternal = 12,
|
||||
UdiEmbedded = 13,
|
||||
Sdtvdongle = 14,
|
||||
Internal = 0x80000000,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
#region SdcFlags enum
|
||||
|
||||
[Flags]
|
||||
enum SdcFlags : uint
|
||||
{
|
||||
Zero = 0,
|
||||
|
||||
TopologyInternal = 0x00000001,
|
||||
TopologyClone = 0x00000002,
|
||||
TopologyExtend = 0x00000004,
|
||||
TopologyExternal = 0x00000008,
|
||||
TopologySupplied = 0x00000010,
|
||||
|
||||
UseSuppliedDisplayConfig = 0x00000020,
|
||||
Validate = 0x00000040,
|
||||
Apply = 0x00000080,
|
||||
NoOptimization = 0x00000100,
|
||||
SaveToDatabase = 0x00000200,
|
||||
AllowChanges = 0x00000400,
|
||||
PathPersistIfRequired = 0x00000800,
|
||||
ForceModeEnumeration = 0x00001000,
|
||||
AllowPathOrderChanges = 0x00002000,
|
||||
|
||||
UseDatabaseCurrent = TopologyInternal | TopologyClone | TopologyExtend | TopologyExternal
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigFlags : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
PathActive = 0x00000001
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigSourceStatus
|
||||
{
|
||||
Zero = 0x0,
|
||||
InUse = 0x00000001
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigTargetStatus : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
InUse = 0x00000001,
|
||||
FORCIBLE = 0x00000002,
|
||||
ForcedAvailabilityBoot = 0x00000004,
|
||||
ForcedAvailabilityPath = 0x00000008,
|
||||
ForcedAvailabilitySystem = 0x00000010,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigRotation : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
Identity = 1,
|
||||
Rotate90 = 2,
|
||||
Rotate180 = 3,
|
||||
Rotate270 = 4,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigPixelFormat : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
Pixelformat8Bpp = 1,
|
||||
Pixelformat16Bpp = 2,
|
||||
Pixelformat24Bpp = 3,
|
||||
Pixelformat32Bpp = 4,
|
||||
PixelformatNongdi = 5,
|
||||
PixelformatForceUint32 = 0xffffffff
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigScaling : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
Identity = 1,
|
||||
Centered = 2,
|
||||
Stretched = 3,
|
||||
Aspectratiocenteredmax = 4,
|
||||
Custom = 5,
|
||||
Preferred = 128,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigRational
|
||||
{
|
||||
uint numerator;
|
||||
uint denominator;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigScanLineOrdering : uint
|
||||
{
|
||||
Unspecified = 0,
|
||||
Progressive = 1,
|
||||
Interlaced = 2,
|
||||
InterlacedUpperfieldfirst = Interlaced,
|
||||
InterlacedLowerfieldfirst = 3,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigPathInfo
|
||||
{
|
||||
public DisplayConfigPathSourceInfo sourceInfo;
|
||||
public DisplayConfigPathTargetInfo targetInfo;
|
||||
public uint flags;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigModeInfoType : uint
|
||||
{
|
||||
Zero = 0,
|
||||
|
||||
Source = 1,
|
||||
Target = 2,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
struct DisplayConfigModeInfo
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public DisplayConfigModeInfoType infoType;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public uint id;
|
||||
|
||||
[FieldOffset(8)]
|
||||
public LUID adapterId;
|
||||
|
||||
[FieldOffset(16)]
|
||||
public DisplayConfigTargetMode targetMode;
|
||||
|
||||
[FieldOffset(16)]
|
||||
public DisplayConfigSourceMode sourceMode;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfig2DRegion
|
||||
{
|
||||
uint cx;
|
||||
uint cy;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum D3DmdtVideoSignalStandard : uint
|
||||
{
|
||||
Uninitialized = 0,
|
||||
VesaDmt = 1,
|
||||
VesaGtf = 2,
|
||||
VesaCvt = 3,
|
||||
Ibm = 4,
|
||||
Apple = 5,
|
||||
NtscM = 6,
|
||||
NtscJ = 7,
|
||||
Ntsc443 = 8,
|
||||
PalB = 9,
|
||||
PalB1 = 10,
|
||||
PalG = 11,
|
||||
PalH = 12,
|
||||
PalI = 13,
|
||||
PalD = 14,
|
||||
PalN = 15,
|
||||
PalNc = 16,
|
||||
SecamB = 17,
|
||||
SecamD = 18,
|
||||
SecamG = 19,
|
||||
SecamH = 20,
|
||||
SecamK = 21,
|
||||
SecamK1 = 22,
|
||||
SecamL = 23,
|
||||
SecamL1 = 24,
|
||||
Eia861 = 25,
|
||||
Eia861A = 26,
|
||||
Eia861B = 27,
|
||||
PalK = 28,
|
||||
PalK1 = 29,
|
||||
PalL = 30,
|
||||
PalM = 31,
|
||||
Other = 255
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigVideoSignalInfo
|
||||
{
|
||||
long pixelRate;
|
||||
DisplayConfigRational hSyncFreq;
|
||||
DisplayConfigRational vSyncFreq;
|
||||
DisplayConfig2DRegion activeSize;
|
||||
DisplayConfig2DRegion totalSize;
|
||||
|
||||
D3DmdtVideoSignalStandard videoStandard;
|
||||
DisplayConfigScanLineOrdering ScanLineOrdering;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigTargetMode
|
||||
{
|
||||
DisplayConfigVideoSignalInfo targetVideoSignalInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct PointL
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigSourceMode
|
||||
{
|
||||
uint width;
|
||||
uint height;
|
||||
DisplayConfigPixelFormat pixelFormat;
|
||||
PointL position;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigPathSourceInfo
|
||||
{
|
||||
public LUID adapterId;
|
||||
public uint id;
|
||||
public uint modeInfoIdx;
|
||||
|
||||
public DisplayConfigSourceStatus statusFlags;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct DisplayConfigPathTargetInfo
|
||||
{
|
||||
public LUID adapterId;
|
||||
public uint id;
|
||||
public uint modeInfoIdx;
|
||||
public DisplayConfigVideoOutputTechnology outputTechnology;
|
||||
public DisplayConfigRotation rotation;
|
||||
public DisplayConfigScaling scaling;
|
||||
public DisplayConfigRational refreshRate;
|
||||
public DisplayConfigScanLineOrdering scanLineOrdering;
|
||||
|
||||
public bool targetAvailable;
|
||||
public DisplayConfigTargetStatus statusFlags;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum QueryDisplayFlags : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
AllPaths = 0x00000001,
|
||||
OnlyActivePaths = 0x00000002,
|
||||
DatabaseCurrent = 0x00000004
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum DisplayConfigTopologyId : uint
|
||||
{
|
||||
Zero = 0x0,
|
||||
|
||||
Internal = 0x00000001,
|
||||
Clone = 0x00000002,
|
||||
Extend = 0x00000004,
|
||||
External = 0x00000008,
|
||||
ForceUint32 = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
private enum DISPLAYCONFIG_COLOR_ENCODING
|
||||
{
|
||||
DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
|
||||
DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
|
||||
DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2,
|
||||
DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3,
|
||||
DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
|
||||
{
|
||||
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
|
||||
public uint value;
|
||||
public DISPLAYCONFIG_COLOR_ENCODING colorEncoding;
|
||||
public int bitsPerColorChannel;
|
||||
|
||||
public bool advancedColorSupported => (value & 0x1) == 0x1;
|
||||
public bool advancedColorEnabled => (value & 0x2) == 0x2;
|
||||
public bool wideColorEnforced => (value & 0x4) == 0x4;
|
||||
public bool advancedColorForceDisabled => (value & 0x8) == 0x8;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO
|
||||
{
|
||||
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
|
||||
public uint enableAdvancedColor;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
|
||||
{
|
||||
public uint value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME
|
||||
{
|
||||
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
|
||||
public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
|
||||
public DisplayConfigVideoOutputTechnology outputTechnology;
|
||||
public ushort edidManufactureId;
|
||||
public ushort edidProductCodeId;
|
||||
public uint connectorInstance;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string monitorFriendlyDeviceName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
public string monitorDevicePat;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
|
||||
{
|
||||
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string viewGdiDeviceName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static class NativeMethods
|
||||
{
|
||||
[DllImport("User32.dll")]
|
||||
public static extern int GetDisplayConfigBufferSizes(QueryDisplayFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
public static extern int SetDisplayConfig(uint numPathArrayElements, [In] DisplayConfigPathInfo[] pathArray, uint numModeInfoArrayElements, [In] DisplayConfigModeInfo[] modeInfoArray, SdcFlags flags);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
public static extern int QueryDisplayConfig(QueryDisplayFlags flags, ref uint numPathArrayElements, [In, Out] DisplayConfigPathInfo[] pathInfoArray, ref uint modeInfoArrayElements, [In, Out] DisplayConfigModeInfo[] modeInfoArray, out DisplayConfigTopologyId id);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO setPacket);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME requestPacket);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using Windows.Devices.Enumeration;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Devices.Enumeration;
|
||||
using Windows.Devices.Enumeration.Pnp;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class PnpDev
|
||||
{
|
||||
[JsonIgnore]
|
||||
public DeviceInformation DevInfo { get; private set; }
|
||||
[JsonIgnore]
|
||||
public PnpObject PnpObject { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
@@ -13,18 +16,27 @@ namespace ColorControl
|
||||
|
||||
public string MacAddress { get; private set; }
|
||||
|
||||
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress)
|
||||
[JsonConstructor]
|
||||
public PnpDev(string name, string ipAddress, string macAddress)
|
||||
{
|
||||
DevInfo = devInfo;
|
||||
PnpObject = pnpObject;
|
||||
Name = name;
|
||||
IpAddress = ipAddress;
|
||||
MacAddress = macAddress;
|
||||
}
|
||||
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress) : this(name, ipAddress, macAddress)
|
||||
{
|
||||
DevInfo = devInfo;
|
||||
PnpObject = pnpObject;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name + " (" + IpAddress + ")";
|
||||
return (IsCustom() ? "Custom: " : "Auto detect: ") + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $"({IpAddress})" : string.Empty);
|
||||
}
|
||||
|
||||
public bool IsCustom()
|
||||
{
|
||||
return DevInfo == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Win32;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
|
||||
@@ -9,16 +10,19 @@ namespace ColorControl
|
||||
public class RestartDetector : IDisposable
|
||||
{
|
||||
public bool RestartDetected { get; private set; }
|
||||
|
||||
public bool PowerOffDetected { get; private set; }
|
||||
|
||||
private EventLogWatcher watcher = null;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private static readonly IEnumerable<string> RestartNames = new[] { "restart", "herstart", "reinicio" };
|
||||
private static readonly IEnumerable<string> PowerOffNames = new[] { "power off", "uitschakelen", "apagar" };
|
||||
|
||||
public RestartDetector()
|
||||
{
|
||||
try
|
||||
{
|
||||
EventLogQuery subscriptionQuery = new EventLogQuery(
|
||||
"System", PathType.LogName, "*[System[Provider[@Name='USER32'] and (EventID=1074)]]");
|
||||
EventLogQuery subscriptionQuery = new EventLogQuery("System", PathType.LogName, "*[System[Provider[@Name='USER32'] and (EventID=1074)]]");
|
||||
|
||||
watcher = new EventLogWatcher(subscriptionQuery);
|
||||
|
||||
@@ -47,6 +51,7 @@ namespace ColorControl
|
||||
public void EventLogEventRead(object obj, EventRecordWrittenEventArgs arg)
|
||||
{
|
||||
RestartDetected = false;
|
||||
PowerOffDetected = false;
|
||||
try
|
||||
{
|
||||
Logger.Debug("Logging event: " + arg);
|
||||
@@ -55,31 +60,20 @@ namespace ColorControl
|
||||
{
|
||||
foreach (EventProperty x in ((EventLogRecord)arg.EventRecord).Properties)
|
||||
{
|
||||
Logger.Debug("Event value: " + x.Value);
|
||||
if (x.Value.Equals("restart"))
|
||||
var strValue = x.Value.ToString();
|
||||
Logger.Debug("Event value: " + strValue);
|
||||
if (RestartNames.Any(n => n.Equals(strValue, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
RestartDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (PowerOffNames.Any(n => n.Equals(strValue, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
PowerOffDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if (arg.EventRecord != null)
|
||||
//{
|
||||
// String[] xPathRefs = new String[1];
|
||||
// xPathRefs[0] = "Event/EventData/Data";
|
||||
// IEnumerable<String> xPathEnum = xPathRefs;
|
||||
|
||||
// EventLogPropertySelector logPropertyContext = new EventLogPropertySelector(xPathEnum);
|
||||
// IList<object> logEventProps = ((EventLogRecord)arg.EventRecord).GetPropertyValues(logPropertyContext);
|
||||
|
||||
// string[] eventData = (string[])logEventProps[0];
|
||||
|
||||
// foreach (string attribute in eventData)
|
||||
// {
|
||||
// if (attribute.Contains("restart")) { restart = true; break; }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,135 @@
|
||||
using NWin32.NativeTypes;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ColorControl.Native
|
||||
{
|
||||
public static class WinApi
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
internal struct POWERBROADCAST_SETTING
|
||||
{
|
||||
public Guid PowerSetting;
|
||||
public uint DataLength;
|
||||
public byte Data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct COPYDATASTRUCT
|
||||
{
|
||||
public IntPtr dwData; // in C/C++ this is an UINT_PTR, not an UINT
|
||||
public int cbData;
|
||||
public IntPtr lpData;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CopyData : IDisposable
|
||||
{
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
|
||||
static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, ref CopyData target,
|
||||
SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult);
|
||||
|
||||
[Flags]
|
||||
enum SendMessageTimeoutFlags : uint
|
||||
{
|
||||
SMTO_NORMAL = 0x0,
|
||||
SMTO_BLOCK = 0x1,
|
||||
SMTO_ABORTIFHUNG = 0x2,
|
||||
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
|
||||
}
|
||||
const uint WM_COPYDATA = 0x4A;
|
||||
|
||||
public IntPtr dwData;
|
||||
public int cbData;
|
||||
public IntPtr lpData;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (lpData != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(lpData);
|
||||
lpData = IntPtr.Zero;
|
||||
cbData = 0;
|
||||
}
|
||||
}
|
||||
public string AsAnsiString
|
||||
{
|
||||
get { return Marshal.PtrToStringAnsi(lpData, cbData); }
|
||||
}
|
||||
public string AsUnicodeString
|
||||
{
|
||||
get { return Marshal.PtrToStringUni(lpData); }
|
||||
}
|
||||
public static CopyData CreateForString(int dwData, string value, bool Unicode = false)
|
||||
{
|
||||
var result = new CopyData();
|
||||
result.dwData = (IntPtr)dwData;
|
||||
result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value);
|
||||
result.cbData = value.Length + 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static UIntPtr Send(IntPtr targetHandle, int dwData, string value, uint timeoutMs = 1000, bool Unicode = false)
|
||||
{
|
||||
var cds = CreateForString(dwData, value, Unicode);
|
||||
UIntPtr result;
|
||||
SendMessageTimeout(targetHandle, WM_COPYDATA, IntPtr.Zero, ref cds, SendMessageTimeoutFlags.SMTO_NORMAL, timeoutMs, out result);
|
||||
cds.Dispose();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MessageFilterInfo : uint
|
||||
{
|
||||
None = 0, AlreadyAllowed = 1, AlreadyDisAllowed = 2, AllowedHigher = 3
|
||||
};
|
||||
|
||||
public enum ChangeWindowMessageFilterExAction : uint
|
||||
{
|
||||
Reset = 0, Allow = 1, DisAllow = 2
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CHANGEFILTERSTRUCT
|
||||
{
|
||||
public uint size;
|
||||
public MessageFilterInfo info;
|
||||
}
|
||||
|
||||
public const int ENUM_CURRENT_SETTINGS = -1;
|
||||
public const int ENUM_REGISTRY_SETTINGS = -2;
|
||||
|
||||
public static int PBT_POWERSETTINGCHANGE = 32787;
|
||||
|
||||
public delegate void PCREATE_PROCESS_NOTIFY_ROUTINE(IntPtr ParentId, IntPtr ProcessId, bool Create);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool EnumDisplaySettingsA(string deviceName, int modeNum, out DEVMODEA devMode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
public static extern bool AttachConsole(int processId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool FreeConsole();
|
||||
|
||||
[DllImport(@"User32", SetLastError = true)]
|
||||
public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);
|
||||
|
||||
[DllImport("User32.dll", SetLastError = true)]
|
||||
public static extern bool UnregisterPowerSettingNotification(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg, ChangeWindowMessageFilterExAction action, ref CHANGEFILTERSTRUCT changeInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// A struct to represent a keybind (key + modifiers)
|
||||
/// When the keybind struct is compared to another keybind struct, the equality is based on the
|
||||
/// modifiers and the virtual key code.
|
||||
/// </summary>
|
||||
internal class KeybindStruct : IEquatable<KeybindStruct>
|
||||
{
|
||||
public readonly int VirtualKeyCode;
|
||||
public readonly List<ModifierKeys> Modifiers;
|
||||
public readonly Guid? Identifier;
|
||||
|
||||
public KeybindStruct(IEnumerable<ModifierKeys> modifiers, int virtualKeyCode, Guid? identifier = null)
|
||||
{
|
||||
this.VirtualKeyCode = virtualKeyCode;
|
||||
this.Modifiers = new List<ModifierKeys>(modifiers);
|
||||
this.Identifier = identifier;
|
||||
}
|
||||
|
||||
public bool Equals(KeybindStruct other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (this.VirtualKeyCode != other.VirtualKeyCode)
|
||||
return false;
|
||||
|
||||
if (this.Modifiers.Count != other.Modifiers.Count)
|
||||
return false;
|
||||
|
||||
foreach (var modifier in this.Modifiers)
|
||||
{
|
||||
if (!other.Modifiers.Contains(modifier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
|
||||
return Equals((KeybindStruct) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 13;
|
||||
hash = (hash * 7) + VirtualKeyCode.GetHashCode();
|
||||
|
||||
// Sum the modifiers separately, so that their order does not affect the final hash
|
||||
var modifiersHashSum = 0;
|
||||
foreach (var modifier in this.Modifiers)
|
||||
{
|
||||
modifiersHashSum += modifier.GetHashCode();
|
||||
}
|
||||
|
||||
hash = (hash * 7) + modifiersHashSum;
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
internal struct KeyboardParams
|
||||
{
|
||||
public IntPtr wParam;
|
||||
public int vkCode;
|
||||
|
||||
public KeyboardParams(IntPtr wParam, int vkCode)
|
||||
{
|
||||
this.wParam = wParam;
|
||||
this.vkCode = vkCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hotkey manager that uses a low-level global keyboard hook, but eventually only fires events for
|
||||
/// pre-registered hotkeys, i.e. not invading a user's privacy.
|
||||
/// </summary>
|
||||
public class KeyboardHookManager
|
||||
{
|
||||
#region Private Attributes
|
||||
/// <summary>
|
||||
/// Keeps track of all registered hotkeys
|
||||
/// </summary>
|
||||
private readonly Dictionary<KeybindStruct, Action> _registeredCallbacks;
|
||||
/// <summary>
|
||||
/// Keeps track of modifier keys that are held down
|
||||
/// </summary>
|
||||
private readonly HashSet<ModifierKeys> _downModifierKeys;
|
||||
/// <summary>
|
||||
/// Keeps track of all keys that are held down to prevent firing callbacks
|
||||
/// more than once for a single keypress
|
||||
/// </summary>
|
||||
private readonly HashSet<int> _downKeys;
|
||||
private readonly object _modifiersLock = new object();
|
||||
private LowLevelKeyboardProc _hook;
|
||||
private bool _isStarted;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Instantiates an empty keyboard hook manager.
|
||||
/// It is best practice to keep a single instance per process.
|
||||
/// Start() must be called to start the low-level keyboard hook manager
|
||||
/// </summary>
|
||||
public KeyboardHookManager()
|
||||
{
|
||||
this._registeredCallbacks = new Dictionary<KeybindStruct, Action>();
|
||||
this._downModifierKeys = new HashSet<ModifierKeys>();
|
||||
this._downKeys = new HashSet<int>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// Starts the low-level keyboard hook.
|
||||
/// Hotkeys can be registered regardless of the low-level keyboard hook's state, but their callbacks
|
||||
/// will only ever be invoked if the low-level keyboard hook is running and intercepting keys.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (this._isStarted) return;
|
||||
|
||||
this._hook = this.HookCallback;
|
||||
_hookId = SetHook(this._hook);
|
||||
this._isStarted = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the low-level keyboard hook (without unregistering the existing hotkeys)
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (this._isStarted)
|
||||
{
|
||||
UnhookWindowsHookEx(_hookId);
|
||||
this._isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a hotkey.
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the hotkey</param>
|
||||
/// <param name="action">The callback action to invoke when this hotkey is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key is already mapped to a callback</exception>
|
||||
/// /// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(int virtualKeyCode, Action action)
|
||||
{
|
||||
return this.RegisterHotkey(new ModifierKeys[0], virtualKeyCode, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new key combination with one or more modifiers
|
||||
/// </summary>
|
||||
/// <param name="modifiers">Modifiers that must be held while hitting the key. Multiple modifiers can be provided using the flags bitwise OR operation</param>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the standard key</param>
|
||||
/// <param name="action">The callback action to invoke when this combination is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key combination is already mapped to a callback</exception>
|
||||
/// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(ModifierKeys modifiers, int virtualKeyCode, Action action)
|
||||
{
|
||||
var allModifiers = Enum.GetValues(typeof(ModifierKeys)).Cast<ModifierKeys>().ToArray();
|
||||
|
||||
// Get the modifiers that were chained with bitwise OR operation as an array of modifiers
|
||||
var selectedModifiers = allModifiers.Where(modifier => modifiers.HasFlag(modifier)).ToArray();
|
||||
|
||||
return RegisterHotkey(selectedModifiers, virtualKeyCode, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new key combination.
|
||||
/// </summary>
|
||||
/// <param name="modifiers">Modifiers that must be held while hitting the key</param>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the standard key</param>
|
||||
/// <param name="action">The callback action to invoke when this combination is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key combination is already mapped to a callback</exception>
|
||||
/// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(ModifierKeys[] modifiers, int virtualKeyCode, Action action)
|
||||
{
|
||||
var keybindIdentity = Guid.NewGuid();
|
||||
var keybind = new KeybindStruct(modifiers, virtualKeyCode, keybindIdentity);
|
||||
if (this._registeredCallbacks.ContainsKey(keybind))
|
||||
{
|
||||
throw new HotkeyAlreadyRegisteredException();
|
||||
}
|
||||
|
||||
this._registeredCallbacks[keybind] = action;
|
||||
return keybindIdentity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters all hotkeys (the low-level keyboard hook continues running)
|
||||
/// </summary>
|
||||
public void UnregisterAll()
|
||||
{
|
||||
this._registeredCallbacks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific single-key hotkey
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode">Virtual key code of the unregistered key</param>
|
||||
/// <exception cref="HotkeyNotRegisteredException">Thrown when the given key combination is not registered</exception>
|
||||
public void UnregisterHotkey(int virtualKeyCode)
|
||||
{
|
||||
this.UnregisterHotkey(new ModifierKeys[0], virtualKeyCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific key combination
|
||||
/// </summary>
|
||||
/// <param name="modifiers">The modifiers of the combination</param>
|
||||
/// <param name="virtualKeyCode">The key of the combination</param>
|
||||
/// <exception cref="HotkeyNotRegisteredException">Thrown when the given key combination is not registered</exception>
|
||||
public void UnregisterHotkey(ModifierKeys[] modifiers, int virtualKeyCode)
|
||||
{
|
||||
var keybind = new KeybindStruct(modifiers, virtualKeyCode);
|
||||
|
||||
if (!this._registeredCallbacks.Remove(keybind))
|
||||
{
|
||||
throw new HotkeyNotRegisteredException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific key by its unique identifier
|
||||
/// </summary>
|
||||
/// <param name="keybindIdentity">Keybind GUID, as returned by RegisterHotkey</param>
|
||||
public void UnregisterHotkey(Guid keybindIdentity)
|
||||
{
|
||||
var keybindToRemove = this._registeredCallbacks.Keys.FirstOrDefault(keybind =>
|
||||
keybind.Identifier.HasValue && keybind.Identifier.Value.Equals(keybindIdentity));
|
||||
|
||||
if (keybindToRemove == null || !this._registeredCallbacks.Remove(keybindToRemove))
|
||||
{
|
||||
throw new HotkeyNotRegisteredException();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
private void HandleKeyPress(int virtualKeyCode)
|
||||
{
|
||||
var currentKey = new KeybindStruct(this._downModifierKeys, virtualKeyCode);
|
||||
|
||||
if (!this._registeredCallbacks.ContainsKey(currentKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._registeredCallbacks.TryGetValue(currentKey, out var callback))
|
||||
{
|
||||
callback.Invoke();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Low level keyboard hook
|
||||
// Source: https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/
|
||||
private const int WH_KEYBOARD_LL = 13;
|
||||
private const int WM_KEYDOWN = 0x0100;
|
||||
private const int WM_KEYUP = 0x0101;
|
||||
private const int WM_SYSKEYDOWN = 0x0104;
|
||||
private const int WM_SYSKEYUP = 0x0105;
|
||||
|
||||
private static IntPtr _hookId = IntPtr.Zero;
|
||||
|
||||
private static IntPtr SetHook(LowLevelKeyboardProc proc)
|
||||
{
|
||||
var userLibrary = LoadLibrary("User32");
|
||||
|
||||
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
|
||||
userLibrary, 0);
|
||||
}
|
||||
|
||||
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
var vkCode = Marshal.ReadInt32(lParam);
|
||||
|
||||
// Debug.WriteLine("Starting");
|
||||
// To prevent slowing keyboard input down, we use handle keyboard inputs in a separate thread
|
||||
ThreadPool.QueueUserWorkItem(this.HandleSingleKeyboardInput, new KeyboardParams(wParam, vkCode));
|
||||
// Debug.WriteLine("Ending");
|
||||
}
|
||||
|
||||
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a keyboard event based on the KeyboardParams it receives
|
||||
/// </summary>
|
||||
/// <param name="keyboardParamsObj">KeyboardParams struct (object type to comply with QueueUserWorkItem)</param>
|
||||
private void HandleSingleKeyboardInput(object keyboardParamsObj)
|
||||
{
|
||||
var keyboardParams = (KeyboardParams)keyboardParamsObj;
|
||||
var wParam = keyboardParams.wParam;
|
||||
var vkCode = keyboardParams.vkCode;
|
||||
|
||||
var modifierKey = ModifierKeysUtilities.GetModifierKeyFromCode(vkCode);
|
||||
|
||||
// If the keyboard event is a KeyDown event (i.e. key pressed)
|
||||
if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
|
||||
{
|
||||
// In this case, we only care about modifier keys
|
||||
if (modifierKey != null)
|
||||
{
|
||||
lock (this._modifiersLock)
|
||||
{
|
||||
this._downModifierKeys.Add(modifierKey.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger callbacks that are registered for this key, but only once per key press
|
||||
if (!this._downKeys.Contains(vkCode))
|
||||
{
|
||||
this.HandleKeyPress(vkCode);
|
||||
this._downKeys.Add(vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
// If the keyboard event is a KeyUp event (i.e. key released)
|
||||
if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
|
||||
{
|
||||
// If the released key is a modifier key, remove it from the HashSet of modifier keys
|
||||
if (modifierKey != null)
|
||||
{
|
||||
lock (this._modifiersLock)
|
||||
{
|
||||
this._downModifierKeys.Remove(modifierKey.Value);
|
||||
}
|
||||
}
|
||||
|
||||
this._downKeys.Remove(vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr SetWindowsHookEx(int idHook,
|
||||
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
|
||||
IntPtr wParam, IntPtr lParam);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the library.
|
||||
/// </summary>
|
||||
/// <param name="lpFileName">Name of the library</param>
|
||||
/// <returns>A handle to the library</returns>
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Exceptions
|
||||
public class NonInvasiveKeyboardHookException : Exception
|
||||
{
|
||||
}
|
||||
|
||||
public class HotkeyAlreadyRegisteredException : NonInvasiveKeyboardHookException
|
||||
{
|
||||
}
|
||||
|
||||
public class HotkeyNotRegisteredException : NonInvasiveKeyboardHookException
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
[Flags]
|
||||
public enum ModifierKeys
|
||||
{
|
||||
Alt = 1,
|
||||
Control = 2,
|
||||
Shift = 4,
|
||||
WindowsKey = 8,
|
||||
}
|
||||
|
||||
public static class ModifierKeysUtilities
|
||||
{
|
||||
public static ModifierKeys? GetModifierKeyFromCode(int keyCode)
|
||||
{
|
||||
switch (keyCode)
|
||||
{
|
||||
case 0xA0:
|
||||
case 0xA1:
|
||||
case 0x10:
|
||||
return ModifierKeys.Shift;
|
||||
|
||||
case 0xA2:
|
||||
case 0xA3:
|
||||
case 0x11:
|
||||
return ModifierKeys.Control;
|
||||
|
||||
case 0x12:
|
||||
case 0xA4:
|
||||
case 0xA5:
|
||||
return ModifierKeys.Alt;
|
||||
|
||||
case 0x5B:
|
||||
case 0x5C:
|
||||
return ModifierKeys.WindowsKey;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
using NvAPIWrapper;
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
using NvAPIWrapper.Native.Display.Structures;
|
||||
using NvAPIWrapper.Native.GPU.Structures;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using Windows.Graphics.Display;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
enum NvDitherBits
|
||||
{
|
||||
[Description("6-bit")]
|
||||
Bits6 = 0,
|
||||
[Description("8-bit")]
|
||||
Bits8 = 1,
|
||||
[Description("10-bit")]
|
||||
Bits10 = 2
|
||||
}
|
||||
|
||||
enum NvDitherMode
|
||||
{
|
||||
[Description("Spatial Dynamic")]
|
||||
SpatialDynamic = 0,
|
||||
[Description("Spatial Static")]
|
||||
SpatialStatic = 1,
|
||||
[Description("Spatial Dynamic 2x2")]
|
||||
SpatialDynamic2x2 = 2,
|
||||
[Description("Spatial Static 2x2")]
|
||||
SpatialStatic2x2 = 3,
|
||||
[Description("Temporal")]
|
||||
Temporal = 4
|
||||
}
|
||||
|
||||
class NvService : GraphicsService
|
||||
{
|
||||
[DllImport(@"nvapi64", EntryPoint = @"nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl,
|
||||
PreserveSig = true)]
|
||||
private static extern IntPtr NvAPI64_QueryInterface(uint interfaceId);
|
||||
|
||||
public delegate long NvAPI_Disp_SetDitherControl(
|
||||
[In] PhysicalGPUHandle physicalGpu,
|
||||
[In] uint OutputId,
|
||||
[In] uint state,
|
||||
[In] uint bits,
|
||||
[In] uint mode
|
||||
);
|
||||
|
||||
public delegate long NvAPI_Disp_GetDitherControl(
|
||||
[In] PhysicalGPUHandle physicalGpu,
|
||||
[In] uint OutputId,
|
||||
[In] IntPtr ditherControl);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NV_GPU_DITHER_CONTROL_V1
|
||||
{
|
||||
public int version;
|
||||
public uint state;
|
||||
public uint bits;
|
||||
public uint mode;
|
||||
};
|
||||
|
||||
private List<NvPreset> _presets;
|
||||
|
||||
private Display _currentDisplay;
|
||||
private NvPreset _lastAppliedPreset;
|
||||
|
||||
public NvService(string dataPath) : base(dataPath)
|
||||
{
|
||||
var converter = new ColorDataConverter();
|
||||
_JsonDeserializer.RegisterConverters(new[] { converter });
|
||||
|
||||
LoadPresets();
|
||||
}
|
||||
|
||||
public Display GetCurrentDisplay()
|
||||
{
|
||||
if (_currentDisplay == null)
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays()[0];
|
||||
}
|
||||
|
||||
return _currentDisplay;
|
||||
}
|
||||
|
||||
public bool HasDisplaysAttached()
|
||||
{
|
||||
try
|
||||
{
|
||||
return DisplayDevice.GetGDIPrimaryDisplayDevice() != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
public List<NvPreset> GetPresets()
|
||||
{
|
||||
return _presets;
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, "NvPresets.json");
|
||||
|
||||
if (File.Exists(_presetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<NvPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presets = NvPreset.GetDefaultPresets();
|
||||
}
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = _JsonSerializer.Serialize(_presets);
|
||||
File.WriteAllText(_presetsFilename, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e.ToLogString());
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCurrentDisplay(NvPreset preset)
|
||||
{
|
||||
if (preset.primaryDisplay)
|
||||
{
|
||||
var displayId = DisplayDevice.GetGDIPrimaryDisplayDevice().DisplayId;
|
||||
_currentDisplay = Display.GetDisplays().FirstOrDefault(x => x.DisplayDevice.DisplayId == displayId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays().FirstOrDefault(x => x.Name.Equals(preset.displayName));
|
||||
}
|
||||
}
|
||||
|
||||
public bool ApplyPreset(NvPreset preset, Config config)
|
||||
{
|
||||
var result = true;
|
||||
|
||||
if (!HasDisplaysAttached())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetCurrentDisplay(preset);
|
||||
|
||||
var hdrEnabled = IsHDREnabled();
|
||||
|
||||
var newHdrEnabled = preset.applyHDR && (preset.HDREnabled || (preset.toggleHDR && !hdrEnabled));
|
||||
var applyHdr = preset.applyHDR && (preset.toggleHDR || preset.HDREnabled != hdrEnabled);
|
||||
|
||||
if (preset.applyColorData && (ColorDataDiffers(preset.colorData) || (!newHdrEnabled && preset.applyColorData && preset.colorData.Colorimetry != ColorDataColorimetry.Auto)))
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (hdrEnabled)
|
||||
{
|
||||
SetHDRState(display, false);
|
||||
|
||||
applyHdr = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
display.DisplayDevice.SetColorData(preset.colorData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"SetColorData threw an exception: {ex.Message}");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (hdrEnabled && newHdrEnabled)
|
||||
{
|
||||
SetHDRState(display, true);
|
||||
|
||||
applyHdr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (applyHdr)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
|
||||
var colorData = preset.applyColorData ? preset.colorData : display.DisplayDevice.CurrentColorData;
|
||||
|
||||
SetHDRState(display, newHdrEnabled, colorData);
|
||||
}
|
||||
|
||||
if (preset.applyRefreshRate)
|
||||
{
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.applyDithering)
|
||||
{
|
||||
if (!SetDithering(preset.ditheringEnabled, preset: preset))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ColorData GetCurrentColorData(Display display)
|
||||
{
|
||||
try
|
||||
{
|
||||
return display.DisplayDevice.CurrentColorData;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Error while reading current color data: " + e.Message);
|
||||
return new ColorData();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ColorDataDiffers(ColorData colorData)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
|
||||
var currentColorData = GetCurrentColorData(display);
|
||||
|
||||
var settingRange = colorData.DynamicRange == ColorDataDynamicRange.Auto ? colorData.ColorFormat == ColorDataFormat.RGB ? ColorDataDynamicRange.VESA : ColorDataDynamicRange.CEA : colorData.DynamicRange;
|
||||
|
||||
var settingSpace = colorData.Colorimetry;
|
||||
|
||||
return colorData.ColorFormat != currentColorData.ColorFormat || colorData.ColorDepth != currentColorData.ColorDepth || settingRange != currentColorData.DynamicRange || settingSpace != currentColorData.Colorimetry;
|
||||
}
|
||||
|
||||
private void SetHDRState(Display display, bool enabled, ColorData colorData = null)
|
||||
{
|
||||
var newMaster = new MasteringDisplayColorData();
|
||||
var hdr = new HDRColorData(enabled ? ColorDataHDRMode.UHDA : ColorDataHDRMode.Off, newMaster, colorData?.ColorFormat, colorData?.DynamicRange, colorData?.ColorDepth);
|
||||
|
||||
display.DisplayDevice.SetHDRColorData(hdr);
|
||||
|
||||
// HDR will not always be disabled this way, then we can only disable it through the display settings
|
||||
if (!enabled && IsHDREnabled())
|
||||
{
|
||||
ToggleHDR();
|
||||
}
|
||||
else if (enabled)
|
||||
{
|
||||
// Currectly there seems to be a bug that after enabling HDR via NVAPI, some settings are only applied upon opening the Display Settings...
|
||||
////OpenDisplaySettings();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetDithering(bool enabled, uint bits = 1, uint mode = 4, NvPreset preset = null)
|
||||
{
|
||||
var result = true;
|
||||
|
||||
var ptr = NvAPI64_QueryInterface(0xDF0DFCDD);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_SetDitherControl)) as NvAPI_Disp_SetDitherControl;
|
||||
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
|
||||
var gpuHandle = displayDevice.PhysicalGPU.Handle;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
|
||||
if (preset != null)
|
||||
{
|
||||
bits = preset.ditheringBits;
|
||||
mode = preset.ditheringMode;
|
||||
}
|
||||
|
||||
var resultValue = delegateValue(gpuHandle, displayId, (uint)(enabled ? 1 : 2), bits, mode);
|
||||
if (resultValue != 0)
|
||||
{
|
||||
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"Could not set dithering because the function NvAPI_Disp_SetDitherControl could not be found");
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool GetDithering()
|
||||
{
|
||||
// Requires elevation, too bad :(
|
||||
//Utils.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\nvlddmkm\State\DisplayDatabase", "DitherRegistryKey");
|
||||
|
||||
/*
|
||||
var ptr = NvAPI64_QueryInterface(0x932AC8FB);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl;
|
||||
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
|
||||
var gpuHandle = displayDevice.PhysicalGPU.Handle;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
|
||||
NV_GPU_DITHER_CONTROL_V1 info = new NV_GPU_DITHER_CONTROL_V1();
|
||||
info.version = 1;
|
||||
var size = Marshal.SizeOf(info.GetType());
|
||||
IntPtr bla = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(info, bla, false);
|
||||
|
||||
// Does not work yet...What is the exact interface of NvAPI_Disp_GetDitherControl?
|
||||
|
||||
var result = delegateValue(gpuHandle, displayId, bla);
|
||||
if (result != 0)
|
||||
{
|
||||
Logger.Error($"Could not get dithering because NvAPI_Disp_GetDitherControl returned a non-zero return code: {result}");
|
||||
}
|
||||
|
||||
return info.state == 1;
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetRefreshRate(uint refreshRate)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
if (timing.Extra.RefreshRate == refreshRate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
|
||||
return SetRefreshRateInternal(display.Name, refreshRate, portrait, timing.HorizontalVisible, timing.VerticalVisible);
|
||||
}
|
||||
|
||||
public List<uint> GetAvailableRefreshRates(NvPreset preset = null)
|
||||
{
|
||||
if (preset != null)
|
||||
{
|
||||
SetCurrentDisplay(preset);
|
||||
}
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
return GetAvailableRefreshRatesInternal(display.Name, portrait, timing.HorizontalVisible, timing.VerticalVisible);
|
||||
}
|
||||
|
||||
public bool IsHDREnabled()
|
||||
{
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
var hdr = displayDevice.HDRColorData;
|
||||
return hdr?.HDRMode == ColorDataHDRMode.UHDA;
|
||||
}
|
||||
|
||||
public Display[] GetDisplays()
|
||||
{
|
||||
return Display.GetDisplays();
|
||||
}
|
||||
|
||||
public List<NvDisplayInfo> GetDisplayInfos()
|
||||
{
|
||||
Display[] displays;
|
||||
try
|
||||
{
|
||||
displays = GetDisplays();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Error while getting displays: " + e.ToLogString());
|
||||
return null;
|
||||
}
|
||||
|
||||
var list = new List<NvDisplayInfo>();
|
||||
|
||||
foreach (var display in displays)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
var name = display.Name;
|
||||
|
||||
var screen = Screen.AllScreens.FirstOrDefault(x => x.DeviceName.Equals(name));
|
||||
if (screen != null)
|
||||
{
|
||||
name += " (" + screen.DeviceFriendlyName() + ")";
|
||||
}
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var colorData = GetCurrentColorData(display);
|
||||
|
||||
var colorSettings = string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry);
|
||||
|
||||
values.Add(colorSettings);
|
||||
|
||||
var refreshRate = display.DisplayDevice.CurrentTiming.Extra.RefreshRate;
|
||||
|
||||
values.Add($"{refreshRate}Hz");
|
||||
|
||||
var lastPreset = GetLastAppliedPreset();
|
||||
if (lastPreset != null)
|
||||
{
|
||||
values.Add(lastPreset.GetDitheringDescription());
|
||||
}
|
||||
else
|
||||
{
|
||||
values.Add(string.Empty);
|
||||
}
|
||||
|
||||
var hdrEnabled = IsHDREnabled();
|
||||
values.Add(hdrEnabled ? "Yes" : "No");
|
||||
|
||||
var infoLine = string.Format("{0}: {1}, {2}Hz, HDR: {3}", name, colorSettings, refreshRate, hdrEnabled ? "Yes" : "No");
|
||||
|
||||
var displayInfo = new NvDisplayInfo(display, values, infoLine);
|
||||
|
||||
list.Add(displayInfo);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public NvPreset GetLastAppliedPreset()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
NVIDIA.Initialize();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected override void Uninitialize()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
NVIDIA.Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
internal abstract class PresetBase
|
||||
{
|
||||
public string shortcut { get; set; }
|
||||
|
||||
private int _id;
|
||||
|
||||
public int id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == 0 || ids.Contains(value))
|
||||
{
|
||||
value = GetNewId();
|
||||
}
|
||||
else
|
||||
{
|
||||
ids.Add(value);
|
||||
}
|
||||
_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<int> ids = new List<int>();
|
||||
|
||||
public static int GetNewId()
|
||||
{
|
||||
int id;
|
||||
do
|
||||
{
|
||||
id = new Random().Next();
|
||||
}
|
||||
while (ids.Contains(id));
|
||||
|
||||
ids.Add(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public abstract List<string> GetDisplayValues(Config config = null);
|
||||
}
|
||||
}
|
||||
+315
-27
@@ -1,66 +1,129 @@
|
||||
using NWin32;
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Forms;
|
||||
using ColorControl.Services.AMD;
|
||||
using ColorControl.Services.LG;
|
||||
using ColorControl.Services.NVIDIA;
|
||||
using ColorControl.Svc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
public const string TS_TASKNAME = "ColorControl";
|
||||
public static string DataDir { get; private set; }
|
||||
public static string ConfigFilename { get; private set; }
|
||||
public static string LogFilename { get; private set; }
|
||||
public static Config Config { get; private set; }
|
||||
public static AppContext AppContext { get; private set; }
|
||||
|
||||
public static string MutexId { get; private set; }
|
||||
|
||||
public static bool IsRestarting { get; private set; }
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private static Mutex _mutex;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
DataDir = Utils.GetDataPath();
|
||||
|
||||
//Utils.UpdateFiles(@"H:\temp\ColorControl\ColorControl", @"C:\Users\vinni\source\repos\ColorControl\ColorControl\bin\Debug\net6.0-windows10.0.20348.0");
|
||||
|
||||
var runAsService = args.Contains("--service") || Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
|
||||
|
||||
InitLogger(runAsService);
|
||||
|
||||
Logger.Debug($"Using data path: {DataDir}");
|
||||
Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
|
||||
|
||||
if (runAsService)
|
||||
{
|
||||
Utils.WaitForTask(RunService(args));
|
||||
return;
|
||||
}
|
||||
|
||||
MutexId = $"Global\\{typeof(MainForm).GUID}";
|
||||
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
// Handler for unhandled exceptions.
|
||||
currentDomain.UnhandledException += GlobalUnhandledExceptionHandler;
|
||||
// Handler for exceptions in threads behind forms.
|
||||
Application.ThreadException += GlobalThreadExceptionHandler;
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
LoadConfig();
|
||||
|
||||
if (startUpParams.ActivateChromeFontFix || startUpParams.DeactivateChromeFontFix)
|
||||
if (Config.UseGdiScaling)
|
||||
{
|
||||
Utils.InstallChromeFix(startUpParams.ActivateChromeFontFix);
|
||||
return;
|
||||
Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
|
||||
}
|
||||
|
||||
string mutexId = $"Global\\{typeof(MainForm).GUID}";
|
||||
bool mutexCreated;
|
||||
var mutex = new Mutex(true, mutexId, out mutexCreated);
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
AppContext = new AppContext(Config, startUpParams);
|
||||
|
||||
var existingProcess = Utils.GetProcessByName("ColorControl");
|
||||
|
||||
try
|
||||
{
|
||||
if (HandleStartupParams(startUpParams, existingProcess))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utils.CloseConsole();
|
||||
}
|
||||
|
||||
_mutex = new Mutex(true, MutexId, out var mutexCreated);
|
||||
try
|
||||
{
|
||||
if (!mutexCreated)
|
||||
{
|
||||
var currentProcessId = Process.GetCurrentProcess().Id;
|
||||
foreach (var process in Process.GetProcesses())
|
||||
if (existingProcess != null && existingProcess.Threads.Count > 0)
|
||||
{
|
||||
if (process.ProcessName.Equals("ColorControl") && process.Id != currentProcessId)
|
||||
{
|
||||
if (process.Threads.Count > 0)
|
||||
{
|
||||
var thread = process.Threads[0];
|
||||
var thread = existingProcess.Threads[0];
|
||||
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
|
||||
|
||||
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
|
||||
}
|
||||
else
|
||||
{
|
||||
_mutex.WaitOne();
|
||||
try
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
Utils.StartService();
|
||||
}
|
||||
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm(startUpParams));
|
||||
Application.Run(new MainForm(AppContext));
|
||||
|
||||
if (Debugger.IsAttached && !IsRestarting)
|
||||
{
|
||||
Utils.StopService();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -72,11 +135,228 @@ namespace ColorControl
|
||||
{
|
||||
if (mutexCreated)
|
||||
{
|
||||
mutex.Dispose();
|
||||
_mutex?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
IsRestarting = true;
|
||||
|
||||
_mutex?.Close();
|
||||
_mutex = null;
|
||||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
Application.Restart();
|
||||
|
||||
Thread.Sleep(1000);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private static void InitLogger(bool runAsService)
|
||||
{
|
||||
var config = new NLog.Config.LoggingConfiguration();
|
||||
|
||||
// Targets where to log to: File and Console
|
||||
var logFileAppendix = runAsService ? "_svc" : "";
|
||||
|
||||
LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
|
||||
|
||||
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
|
||||
|
||||
//var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
|
||||
|
||||
// Rules for mapping loggers to targets
|
||||
//config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
|
||||
config.AddRule(LogLevel.Trace, LogLevel.Fatal, logfile);
|
||||
|
||||
// Apply config
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
private static void LoadConfig()
|
||||
{
|
||||
ConfigFilename = Path.Combine(DataDir, "Settings.json");
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(ConfigFilename))
|
||||
{
|
||||
var data = File.ReadAllText(ConfigFilename);
|
||||
Config = JsonConvert.DeserializeObject<Config>(data);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new Config();
|
||||
|
||||
Utils.UseDedicatedElevatedProcess = Config.UseDedicatedElevatedProcess;
|
||||
}
|
||||
|
||||
public static bool HandleStartupParams(StartUpParams startUpParams, Process existingProcess)
|
||||
{
|
||||
if (startUpParams.ActivateChromeFontFix || startUpParams.DeactivateChromeFontFix)
|
||||
{
|
||||
Utils.InstallChromeFix(startUpParams.ActivateChromeFontFix);
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.EnableAutoStart || startUpParams.DisableAutoStart)
|
||||
{
|
||||
Utils.RegisterTask(TS_TASKNAME, startUpParams.EnableAutoStart, startUpParams.AutoStartRunLevel);
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.SetProcessAffinity)
|
||||
{
|
||||
Utils.SetProcessAffinity(startUpParams.ProcessId, startUpParams.AffinityMask);
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.SetProcessPriority)
|
||||
{
|
||||
Utils.SetProcessPriority(startUpParams.ProcessId, startUpParams.PriorityClass);
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.StartElevated)
|
||||
{
|
||||
StartElevated();
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.SendWol)
|
||||
{
|
||||
return WOL.WakeFunction(startUpParams.WolMacAddress, startUpParams.WolIpAddress);
|
||||
}
|
||||
if (startUpParams.InstallService)
|
||||
{
|
||||
Utils.InstallService();
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.UninstallService)
|
||||
{
|
||||
Utils.UninstallService();
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.StartService)
|
||||
{
|
||||
Utils.StartService();
|
||||
return true;
|
||||
}
|
||||
if (startUpParams.StopService)
|
||||
{
|
||||
Utils.StopService();
|
||||
return true;
|
||||
}
|
||||
|
||||
var useConsole = startUpParams.NoGui || existingProcess != null;
|
||||
|
||||
if (!useConsole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
startUpParams.NoGui = true;
|
||||
|
||||
var result = false;
|
||||
|
||||
if (startUpParams.ExecuteHelp)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
|
||||
Console.WriteLine("\nColorControl CLI");
|
||||
Console.WriteLine("Syntax : ColorControl command options");
|
||||
Console.WriteLine("Commands:");
|
||||
Console.WriteLine("--nvpreset <preset name or id>: execute NVIDIA-preset");
|
||||
Console.WriteLine("--amdpreset <preset name or id>: execute AMD-preset");
|
||||
Console.WriteLine("--lgpreset <preset name> : execute LG-preset");
|
||||
Console.WriteLine("--help : displays this help info");
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine("--nogui: starts command from the command line and will not open GUI (is forced when GUI is already running)");
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (startUpParams.ExecuteLgPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
|
||||
Console.WriteLine($"Executing LG-preset '{startUpParams.LgPresetName}'...");
|
||||
var task = LgService.ExecutePresetAsync(startUpParams.LgPresetName);
|
||||
|
||||
var taskResult = Utils.WaitForTask(task);
|
||||
|
||||
if (taskResult)
|
||||
{
|
||||
Console.WriteLine("Done.");
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (startUpParams.ExecuteNvidiaPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
|
||||
Console.WriteLine($"Executing NVIDIA-preset '{startUpParams.NvidiaPresetIdOrName}'...");
|
||||
var taskResult = NvService.ExecutePresetAsync(startUpParams.NvidiaPresetIdOrName);
|
||||
|
||||
if (taskResult)
|
||||
{
|
||||
Console.WriteLine("Done.");
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (startUpParams.ExecuteAmdPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
|
||||
Console.WriteLine($"Executing AMD-preset '{startUpParams.AmdPresetIdOrName}'...");
|
||||
var taskResult = AmdService.ExecutePresetAsync(startUpParams.AmdPresetIdOrName);
|
||||
|
||||
if (taskResult)
|
||||
{
|
||||
Console.WriteLine("Done.");
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void StartElevated()
|
||||
{
|
||||
try
|
||||
{
|
||||
Application.Run(new ElevatedForm());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error while initializing elevated application: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task RunService(string[] args)
|
||||
{
|
||||
Logger.Debug("RUNNING SERVICE");
|
||||
|
||||
using IHost host = Host.CreateDefaultBuilder(args)
|
||||
.UseWindowsService(options =>
|
||||
{
|
||||
options.ServiceName = "ColorControl Service";
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService<ColorControlBackgroundService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
|
||||
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
|
||||
{
|
||||
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
|
||||
@@ -86,14 +366,22 @@ namespace ColorControl
|
||||
|
||||
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var ex = (Exception)e.ExceptionObject;
|
||||
MessageBox.Show("Unhandled exception: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
GlobalHandleException((Exception)e.ExceptionObject, "Unhandled exception");
|
||||
}
|
||||
|
||||
private static void GlobalThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs e)
|
||||
{
|
||||
var ex = e.Exception;
|
||||
MessageBox.Show("Exception in thread: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
GlobalHandleException(e.Exception, "Exception in thread");
|
||||
}
|
||||
|
||||
private static void GlobalHandleException(Exception exception, string type)
|
||||
{
|
||||
var trace = exception.ToLogString(Environment.StackTrace);
|
||||
var message = $"{type}: {trace}";
|
||||
|
||||
Logger.Error(message);
|
||||
|
||||
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ColorControl")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Maassoft")]
|
||||
[assembly: AssemblyProduct("ColorControl")]
|
||||
[assembly: AssemblyCopyright("Copyright © Maassoft 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("a6005d08-70f0-44cb-a384-9219eba84f21")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||
+72
-26
@@ -8,10 +8,10 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ColorControl.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace ColorControl.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@@ -19,53 +19,99 @@ namespace ColorControl.Properties
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorControl.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap LG_remote {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("LG_remote", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Power;120;136
|
||||
///1;106;228
|
||||
///2;196;228
|
||||
///3;286;228
|
||||
///4;106;296
|
||||
///5;196;296
|
||||
///6;286;296
|
||||
///7;106;362
|
||||
///8;196;362
|
||||
///9;286;362
|
||||
///List;106;425
|
||||
///0;196;425
|
||||
///SAP;286;425
|
||||
///Vol +;98;513
|
||||
///Mute;198;513
|
||||
///Channel +;298;513
|
||||
///Vol -;98;622
|
||||
///Channel -;298;622
|
||||
///Home;78;695
|
||||
///Settings;318;695
|
||||
///Up;200;748
|
||||
///Left;98;852
|
||||
///Enter;200;852
|
||||
///Right;304;852
|
||||
///Down;200;952
|
||||
///Back;80;1000
|
||||
///TV Guide;320;1000
|
||||
///Netflix;92;1076
|
||||
///Inputs;200;1066
|
||||
///Amazon Prime;312;1076
|
||||
///Red;90;1150
|
||||
///Green;164;1150
|
||||
///Yellow;240;1150
|
||||
///Blue;316;1150
|
||||
///Rakuten TV;116;1218
|
||||
///Play;202 [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string RC_coords {
|
||||
get {
|
||||
return ResourceManager.GetString("RC_coords", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
@@ -60,6 +60,7 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
@@ -68,9 +69,10 @@
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
@@ -85,9 +87,10 @@
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
@@ -109,9 +112,16 @@
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="LG_remote" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LG_remote.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="RC_coords" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\RC_coords.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
||||
</data>
|
||||
</root>
|
||||
+9
-13
@@ -8,21 +8,17 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ColorControl.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace ColorControl.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"type":"register",
|
||||
"id":"register_0",
|
||||
"payload":{
|
||||
"forcePairing":false,
|
||||
"pairingType":"PROMPT",
|
||||
"client-key":"CLIENTKEYGOESHERE",
|
||||
"manifest":{
|
||||
"manifestVersion":1,
|
||||
"appVersion":"1.1",
|
||||
"signed":{
|
||||
"created":"20140509",
|
||||
"appId":"com.lge.test",
|
||||
"vendorId":"com.lge",
|
||||
"localizedAppNames":{
|
||||
"":"LG Remote App",
|
||||
"ko-KR":"리모컨 앱",
|
||||
"zxx-XX":"ЛГ Rэмotэ AПП"
|
||||
},
|
||||
"localizedVendorNames":{
|
||||
"":"LG Electronics"
|
||||
},
|
||||
"permissions":[
|
||||
"TEST_SECURE",
|
||||
"CONTROL_INPUT_TEXT",
|
||||
"CONTROL_MOUSE_AND_KEYBOARD",
|
||||
"READ_INSTALLED_APPS",
|
||||
"READ_LGE_SDX",
|
||||
"READ_NOTIFICATIONS",
|
||||
"SEARCH",
|
||||
"WRITE_SETTINGS",
|
||||
"WRITE_NOTIFICATION_ALERT",
|
||||
"CONTROL_POWER",
|
||||
"READ_CURRENT_CHANNEL",
|
||||
"READ_RUNNING_APPS",
|
||||
"READ_UPDATE_INFO",
|
||||
"UPDATE_FROM_REMOTE_APP",
|
||||
"READ_LGE_TV_INPUT_EVENTS",
|
||||
"READ_TV_CURRENT_TIME"
|
||||
],
|
||||
"serial":"2f930e2d2cfe083771f68e4fe7bb07"
|
||||
},
|
||||
"permissions":[
|
||||
"LAUNCH",
|
||||
"LAUNCH_WEBAPP",
|
||||
"APP_TO_APP",
|
||||
"CLOSE",
|
||||
"TEST_OPEN",
|
||||
"TEST_PROTECTED",
|
||||
"CONTROL_AUDIO",
|
||||
"CONTROL_DISPLAY",
|
||||
"CONTROL_INPUT_JOYSTICK",
|
||||
"CONTROL_INPUT_MEDIA_RECORDING",
|
||||
"CONTROL_INPUT_MEDIA_PLAYBACK",
|
||||
"CONTROL_INPUT_TV",
|
||||
"CONTROL_POWER",
|
||||
"CONTROL_TV_SCREEN",
|
||||
"READ_APP_STATUS",
|
||||
"READ_CURRENT_CHANNEL",
|
||||
"READ_INPUT_DEVICE_LIST",
|
||||
"READ_NETWORK_STATE",
|
||||
"READ_RUNNING_APPS",
|
||||
"READ_TV_CHANNEL_LIST",
|
||||
"WRITE_NOTIFICATION_TOAST",
|
||||
"READ_POWER_STATE",
|
||||
"READ_COUNTRY_INFO",
|
||||
"READ_SETTINGS"
|
||||
],
|
||||
"signatures":[
|
||||
{
|
||||
"signatureVersion":1,
|
||||
"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -0,0 +1,37 @@
|
||||
Power;120;136
|
||||
1;106;228
|
||||
2;196;228
|
||||
3;286;228
|
||||
4;106;296
|
||||
5;196;296
|
||||
6;286;296
|
||||
7;106;362
|
||||
8;196;362
|
||||
9;286;362
|
||||
List;106;425
|
||||
0;196;425
|
||||
SAP;286;425
|
||||
Vol +;98;513
|
||||
Mute;198;513
|
||||
Channel +;298;513
|
||||
Vol -;98;622
|
||||
Channel -;298;622
|
||||
Home;78;695
|
||||
Settings;318;695
|
||||
Up;200;748
|
||||
Left;98;852
|
||||
Enter;200;852
|
||||
Right;304;852
|
||||
Down;200;952
|
||||
Back;80;1000
|
||||
TV Guide;320;1000
|
||||
Netflix;92;1076
|
||||
Inputs;200;1066
|
||||
Amazon Prime;312;1076
|
||||
Red;90;1150
|
||||
Green;164;1150
|
||||
Yellow;240;1150
|
||||
Blue;316;1150
|
||||
Rakuten TV;116;1218
|
||||
Play;202;1218
|
||||
Pause;291;1218
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class ServiceBase
|
||||
{
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
protected string _dataPath;
|
||||
protected string _presetsFilename;
|
||||
protected JavaScriptSerializer _JsonSerializer;
|
||||
protected JavaScriptSerializer _JsonDeserializer;
|
||||
protected bool _initialized = false;
|
||||
|
||||
public ServiceBase(string dataPath)
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
|
||||
_JsonSerializer = new JavaScriptSerializer();
|
||||
_JsonDeserializer = new JavaScriptSerializer();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~ServiceBase()
|
||||
{
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
protected virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Uninitialize()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using ADL_CONTEXT_HANDLE = System.IntPtr;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.AMD
|
||||
{
|
||||
enum ADLPixelFormat
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using ATI.ADL;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.AMD
|
||||
{
|
||||
class AmdDisplayInfo
|
||||
{
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using ColorControl.Services.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.AMD
|
||||
{
|
||||
class AmdPreset : PresetBase
|
||||
{
|
||||
@@ -19,7 +20,7 @@ namespace ColorControl
|
||||
public bool primaryDisplay { get; set; }
|
||||
public string displayName { get; set; }
|
||||
|
||||
public AmdPreset()
|
||||
public AmdPreset() : base()
|
||||
{
|
||||
applyColorData = true;
|
||||
applyDithering = false;
|
||||
@@ -32,7 +33,7 @@ namespace ColorControl
|
||||
primaryDisplay = true;
|
||||
}
|
||||
|
||||
public AmdPreset(AmdPreset preset): this()
|
||||
public AmdPreset(AmdPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
@@ -62,13 +63,15 @@ namespace ColorControl
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
//return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" };
|
||||
return new[] { "Display|140", "Color settings (BPC, format)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut", "Apply on startup" };
|
||||
return new[] { "Name", "Display|140", "Color settings (BPC, format)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut", "Apply on startup" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var display = string.Format("{0}", primaryDisplay ? "Primary" : displayName);
|
||||
values.Add(display);
|
||||
|
||||
@@ -97,33 +100,41 @@ namespace ColorControl
|
||||
return presets;
|
||||
}
|
||||
|
||||
public string GetTextForMenuItem()
|
||||
public override string GetTextForMenuItem()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (displayName != null)
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
sb.AppendFormat("Display: {0} / ", displayName);
|
||||
}
|
||||
if (applyColorData)
|
||||
{
|
||||
var colorSettings = string.Format("Format: {0}, {1}", colorDepth, pixelFormat);
|
||||
sb.Append(colorSettings);
|
||||
sb.Append(name);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyRefreshRate)
|
||||
else
|
||||
{
|
||||
sb.AppendFormat("{0}Hz", refreshRate);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDithering)
|
||||
{
|
||||
sb.AppendFormat("Dithering: {0}", ditherState);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyHDR)
|
||||
{
|
||||
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
|
||||
sb.Append(" / ");
|
||||
if (displayName != null)
|
||||
{
|
||||
sb.AppendFormat("Display: {0} / ", displayName);
|
||||
}
|
||||
if (applyColorData)
|
||||
{
|
||||
var colorSettings = string.Format("Format: {0}, {1}", colorDepth, pixelFormat);
|
||||
sb.Append(colorSettings);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyRefreshRate)
|
||||
{
|
||||
sb.AppendFormat("{0}Hz", refreshRate);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDithering)
|
||||
{
|
||||
sb.AppendFormat("Dithering: {0}", ditherState);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyHDR)
|
||||
{
|
||||
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
|
||||
sb.Append(" / ");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString(0, Math.Max(0, sb.Length - 3));
|
||||
@@ -1,62 +1,56 @@
|
||||
using ATI.ADL;
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.AMD
|
||||
{
|
||||
class AmdService : GraphicsService
|
||||
class AmdService : GraphicsService<AmdPreset>
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public override string ServiceName => "AMD";
|
||||
|
||||
private ADLDisplayInfo _currentDisplay;
|
||||
|
||||
private List<AmdPreset> _presets;
|
||||
|
||||
private AmdPreset _lastAppliedPreset;
|
||||
|
||||
public AmdService(string dataPath) : base(dataPath)
|
||||
public AmdService(string dataPath) : base(dataPath, "AmdPresets.json")
|
||||
{
|
||||
LoadPresets();
|
||||
}
|
||||
|
||||
public List<AmdPreset> GetPresets()
|
||||
public static bool ExecutePresetAsync(string idOrName)
|
||||
{
|
||||
return _presets;
|
||||
try
|
||||
{
|
||||
var service = new AmdService(Program.DataDir);
|
||||
|
||||
var result = service.ApplyPreset(idOrName);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Console.WriteLine("Preset not found or error while executing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error executing preset: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public AmdPreset GetLastAppliedPreset()
|
||||
protected override List<AmdPreset> GetDefaultPresets()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, "AmdPresets.json");
|
||||
|
||||
if (File.Exists(_presetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<AmdPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presets = AmdPreset.GetDefaultPresets();
|
||||
}
|
||||
return AmdPreset.GetDefaultPresets();
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = _JsonSerializer.Serialize(_presets);
|
||||
File.WriteAllText(_presetsFilename, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e.ToLogString());
|
||||
}
|
||||
Utils.WriteObject(_presetsFilename, _presets);
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
@@ -64,7 +58,7 @@ namespace ColorControl
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
public bool HasDisplaysAttached()
|
||||
public override bool HasDisplaysAttached(bool reinitialize = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -99,7 +93,20 @@ namespace ColorControl
|
||||
return _currentDisplay;
|
||||
}
|
||||
|
||||
public bool ApplyPreset(AmdPreset preset, Config config)
|
||||
public bool ApplyPreset(string idOrName)
|
||||
{
|
||||
var preset = GetPresetByIdOrName(idOrName);
|
||||
if (preset != null)
|
||||
{
|
||||
return ApplyPreset(preset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ApplyPreset(AmdPreset preset)
|
||||
{
|
||||
SetCurrentDisplay(preset);
|
||||
|
||||
@@ -131,9 +138,9 @@ namespace ColorControl
|
||||
|
||||
if (preset.applyRefreshRate)
|
||||
{
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
{
|
||||
result = false;
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +168,8 @@ namespace ColorControl
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -203,7 +212,7 @@ namespace ColorControl
|
||||
|
||||
var portrait = vertical > horizontal;
|
||||
|
||||
return SetRefreshRateInternal(GetDisplayDeviceName(display), refreshRate, portrait, horizontal, vertical);
|
||||
return SetRefreshRateInternal(GetDisplayDeviceName(display), (int)refreshRate, portrait, horizontal, vertical);
|
||||
}
|
||||
|
||||
public List<uint> GetAvailableRefreshRates(AmdPreset preset = null)
|
||||
@@ -295,6 +304,8 @@ namespace ColorControl
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add("Current settings");
|
||||
|
||||
var screen = GetScreenForDisplay(display);
|
||||
|
||||
var name = GetFullDisplayName(display);
|
||||
@@ -1,4 +1,5 @@
|
||||
using NWin32;
|
||||
using ColorControl.Native;
|
||||
using NWin32;
|
||||
using NWin32.NativeTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -7,19 +8,14 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
class GraphicsService : ServiceBase
|
||||
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase, new()
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool EnumDisplaySettingsA(string deviceName, int modeNum, out DEVMODEA devMode);
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
const int ENUM_CURRENT_SETTINGS = -1;
|
||||
const int ENUM_REGISTRY_SETTINGS = -2;
|
||||
|
||||
public GraphicsService(string dataPath) : base(dataPath)
|
||||
public GraphicsService(string dataPath, string presetsBaseFilename) : base(dataPath, presetsBaseFilename)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~GraphicsService()
|
||||
@@ -27,6 +23,8 @@ namespace ColorControl
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
public abstract bool HasDisplaysAttached(bool reinitialize = false);
|
||||
|
||||
protected void ToggleHDR(int delay = 1000)
|
||||
{
|
||||
Process.Start("ms-settings:display");
|
||||
@@ -54,7 +52,7 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
protected bool SetRefreshRateInternal(string displayName, uint refreshRate, bool portrait, int horizontal, int vertical)
|
||||
protected bool SetRefreshRateInternal(string displayName, int refreshRate, bool portrait, int horizontal, int vertical, bool updateRegistry = false)
|
||||
{
|
||||
uint i = 0;
|
||||
DEVMODEA devMode;
|
||||
@@ -67,7 +65,7 @@ namespace ColorControl
|
||||
{
|
||||
IntPtr bla = Marshal.AllocHGlobal(Marshal.SizeOf(devMode));
|
||||
Marshal.StructureToPtr(devMode, bla, false);
|
||||
var result = NativeMethods.ChangeDisplaySettingsExA(displayName, bla, IntPtr.Zero, 0, IntPtr.Zero);
|
||||
var result = NativeMethods.ChangeDisplaySettingsExA(displayName, bla, IntPtr.Zero, updateRegistry ? (uint)NativeConstants.CDS_UPDATEREGISTRY : 0, IntPtr.Zero);
|
||||
if (result != NativeConstants.DISP_CHANGE_SUCCESSFUL)
|
||||
{
|
||||
Logger.Error($"Could not set refreshrate {refreshRate} on display {displayName} because ChangeDisplaySettingsExA returned a non-zero return code: {result}");
|
||||
@@ -103,11 +101,35 @@ namespace ColorControl
|
||||
{
|
||||
DEVMODEA devMode;
|
||||
// NativeMethods defines modeNum as an 'uint' but we need to pass '-1'
|
||||
if (EnumDisplaySettingsA(displayName, ENUM_CURRENT_SETTINGS, out devMode))
|
||||
if (WinApi.EnumDisplaySettingsA(displayName, WinApi.ENUM_CURRENT_SETTINGS, out devMode))
|
||||
{
|
||||
return devMode.dmDisplayFrequency;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected List<DEVMODEA> GetAvailableResolutionsInternal(string displayName, bool portrait, uint refreshRate = 0)
|
||||
{
|
||||
var list = new List<DEVMODEA>();
|
||||
|
||||
uint i = 0;
|
||||
DEVMODEA devMode;
|
||||
while (NativeMethods.EnumDisplaySettingsA(displayName, i, out devMode))
|
||||
{
|
||||
if ((portrait ? devMode.dmPelsWidth < devMode.dmPelsHeight : devMode.dmPelsWidth > devMode.dmPelsHeight) &&
|
||||
(refreshRate == 0 || devMode.dmDisplayFrequency == refreshRate) &&
|
||||
!list.Any(m => m.dmPelsWidth == devMode.dmPelsWidth && m.dmPelsHeight == devMode.dmPelsHeight))
|
||||
{
|
||||
list.Add(devMode);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
list.Sort((a, b) => (int)(a.dmPelsWidth - b.dmPelsWidth));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using ColorControl.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
enum PresetTriggerType
|
||||
{
|
||||
[Description("None")]
|
||||
None = 0,
|
||||
[Description("Startup")]
|
||||
Startup = 1,
|
||||
[Description("Resume")]
|
||||
Resume = 2,
|
||||
[Description("Shutdown")]
|
||||
Shutdown = 3,
|
||||
[Description("Standby")]
|
||||
Standby = 4,
|
||||
[Description("Reserved5")]
|
||||
Reserved5 = 5,
|
||||
[Description("Process switch")]
|
||||
ProcessSwitch = 6,
|
||||
[Description("Screensaver start")]
|
||||
ScreensaverStart = 7,
|
||||
[Description("Screensaver stop")]
|
||||
ScreensaverStop = 8,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum PresetConditionType
|
||||
{
|
||||
[Description("None")]
|
||||
None = 0,
|
||||
[Description("SDR")]
|
||||
SDR = 1,
|
||||
[Description("HDR")]
|
||||
HDR = 2,
|
||||
[Description("Full Screen")]
|
||||
FullScreen = 4,
|
||||
[Description("Notifications Disabled")]
|
||||
NotificationsDisabled = 8,
|
||||
[Description("G-SYNC Disabled")]
|
||||
GsyncDisabled = 16,
|
||||
[Description("G-SYNC Enabled")]
|
||||
GsyncEnabled = 32
|
||||
}
|
||||
|
||||
class PresetTrigger
|
||||
{
|
||||
public PresetTriggerType Trigger { get; set; }
|
||||
public List<string> IncludedProcesses { get; set; }
|
||||
public List<string> ExcludedProcesses { get; set; }
|
||||
public PresetConditionType Conditions { get; set; }
|
||||
|
||||
public PresetTrigger()
|
||||
{
|
||||
IncludedProcesses = new List<string>();
|
||||
ExcludedProcesses = new List<string>();
|
||||
}
|
||||
|
||||
public bool TriggerActive(PresetTriggerContext context)
|
||||
{
|
||||
var active = Conditions == PresetConditionType.None ||
|
||||
(Conditions.HasFlag(PresetConditionType.SDR) ? !context.IsHDRActive : true) && (Conditions.HasFlag(PresetConditionType.HDR) ? context.IsHDRActive : true) &&
|
||||
(Conditions.HasFlag(PresetConditionType.GsyncDisabled) ? !context.IsGsyncActive : true) && (Conditions.HasFlag(PresetConditionType.GsyncEnabled) ? context.IsGsyncActive : true);
|
||||
var allProcesses = IncludedProcesses.Contains("*");
|
||||
|
||||
if (Trigger == PresetTriggerType.ProcessSwitch)
|
||||
{
|
||||
active = active && (allProcesses || (context.ChangedProcesses?.Any() ?? false));
|
||||
|
||||
if (active)
|
||||
{
|
||||
var included = allProcesses || context.ChangedProcesses.Any(cp => IncludedProcesses.Any(ip => cp.ProcessName.NormEquals(ip)));
|
||||
var excluded = context.ChangedProcesses.Any(cp => ExcludedProcesses.Any(ep => cp.ProcessName.NormEquals(ep)));
|
||||
|
||||
var screenSizeCheck = (!Conditions.HasFlag(PresetConditionType.FullScreen) && !context.ForegroundProcessIsFullScreen) ||
|
||||
(context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen &&
|
||||
(allProcesses || IncludedProcesses.Any(ip => context.ForegroundProcess.ProcessName.NormEquals(ip))));
|
||||
|
||||
var notificationsDisabledCheck = !Conditions.HasFlag(PresetConditionType.NotificationsDisabled) || context.IsNotificationDisabled;
|
||||
|
||||
active = included && !excluded && screenSizeCheck && notificationsDisabledCheck;
|
||||
}
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Trigger == PresetTriggerType.None)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return $"On {Trigger.GetDescription()} of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
|
||||
}
|
||||
|
||||
public static string DisplayProcesses(IEnumerable<string> processes)
|
||||
{
|
||||
return processes.Contains("*") ? "all processes" : string.Join(", ", processes);
|
||||
}
|
||||
}
|
||||
|
||||
class PresetTriggerContext
|
||||
{
|
||||
public bool IsHDRActive { get; set; }
|
||||
public bool IsGsyncActive { get; set; }
|
||||
public Process ForegroundProcess { get; set; }
|
||||
public bool ForegroundProcessIsFullScreen { get; set; }
|
||||
public List<Process> ChangedProcesses { get; set; }
|
||||
public bool IsNotificationDisabled { get; set; }
|
||||
}
|
||||
|
||||
internal abstract class PresetBase
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string shortcut { get; set; }
|
||||
public List<PresetTrigger> Triggers { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool ShowInQuickAccess { get; set; }
|
||||
|
||||
private int _id;
|
||||
|
||||
protected PresetBase()
|
||||
{
|
||||
Triggers = new List<PresetTrigger>();
|
||||
}
|
||||
|
||||
public int id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == 0 || ids.Contains(value))
|
||||
{
|
||||
value = GetNewId();
|
||||
}
|
||||
else
|
||||
{
|
||||
ids.Add(value);
|
||||
}
|
||||
_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<int> ids = new List<int>();
|
||||
|
||||
public static int GetNewId()
|
||||
{
|
||||
int id;
|
||||
do
|
||||
{
|
||||
id = new Random().Next();
|
||||
}
|
||||
while (ids.Contains(id));
|
||||
|
||||
ids.Add(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public abstract List<string> GetDisplayValues(Config config = null);
|
||||
|
||||
public virtual string GetTextForMenuItem()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void UpdateTrigger(PresetTriggerType triggerType, PresetConditionType conditions, string includedProcesses, string excludedProcesses)
|
||||
{
|
||||
if (!Triggers.Any())
|
||||
{
|
||||
Triggers.Add(new PresetTrigger());
|
||||
}
|
||||
|
||||
var trigger = Triggers.First();
|
||||
|
||||
trigger.Trigger = triggerType;
|
||||
trigger.Conditions = conditions;
|
||||
|
||||
if (trigger.IncludedProcesses == null)
|
||||
{
|
||||
trigger.IncludedProcesses = new List<string>();
|
||||
}
|
||||
if (trigger.ExcludedProcesses == null)
|
||||
{
|
||||
trigger.ExcludedProcesses = new List<string>();
|
||||
}
|
||||
Utils.ParseWords(trigger.IncludedProcesses, includedProcesses);
|
||||
Utils.ParseWords(trigger.ExcludedProcesses, excludedProcesses);
|
||||
}
|
||||
|
||||
public PresetTrigger AddTrigger()
|
||||
{
|
||||
if (!Triggers.Any())
|
||||
{
|
||||
Triggers.Add(new PresetTrigger());
|
||||
}
|
||||
|
||||
return Triggers.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
partial class QuickAccessForm<T> : Form where T : PresetBase, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.lblTitle = new System.Windows.Forms.Label();
|
||||
this.tmrHide = new System.Windows.Forms.Timer(this.components);
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// flowPanel
|
||||
//
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||
this.flowPanel.Location = new System.Drawing.Point(14, 33);
|
||||
this.flowPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(270, 339);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnClose.FlatAppearance.BorderColor = System.Drawing.Color.DarkGray;
|
||||
this.btnClose.FlatAppearance.BorderSize = 0;
|
||||
this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnClose.ForeColor = System.Drawing.Color.White;
|
||||
this.btnClose.Location = new System.Drawing.Point(280, 0);
|
||||
this.btnClose.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Size = new System.Drawing.Size(16, 27);
|
||||
this.btnClose.TabIndex = 1;
|
||||
this.btnClose.TabStop = false;
|
||||
this.btnClose.Text = "x";
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// lblTitle
|
||||
//
|
||||
this.lblTitle.AutoSize = true;
|
||||
this.lblTitle.ForeColor = System.Drawing.Color.White;
|
||||
this.lblTitle.Location = new System.Drawing.Point(13, 6);
|
||||
this.lblTitle.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lblTitle.Name = "lblTitle";
|
||||
this.lblTitle.Size = new System.Drawing.Size(77, 15);
|
||||
this.lblTitle.TabIndex = 0;
|
||||
this.lblTitle.Text = "Quick Access";
|
||||
//
|
||||
// tmrHide
|
||||
//
|
||||
this.tmrHide.Interval = 5000;
|
||||
this.tmrHide.Tick += new System.EventHandler(this.tmrHide_Tick);
|
||||
//
|
||||
// QuickAccessForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Black;
|
||||
this.ClientSize = new System.Drawing.Size(298, 387);
|
||||
this.Controls.Add(this.lblTitle);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.flowPanel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.KeyPreview = true;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "QuickAccessForm";
|
||||
this.Opacity = 0.8D;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "RemoteControlForm";
|
||||
this.TopMost = true;
|
||||
this.Activated += new System.EventHandler(this.LgGameBar_Activated);
|
||||
this.Deactivate += new System.EventHandler(this.LgGameBar_Deactivate);
|
||||
this.Shown += new System.EventHandler(this.RemoteControlForm_Shown);
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp);
|
||||
this.Move += new System.EventHandler(this.LgGameBar_Move);
|
||||
this.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.QuickAccessForm_PreviewKeyDown);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
private System.Windows.Forms.Label lblTitle;
|
||||
private System.Windows.Forms.Timer tmrHide;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
partial class QuickAccessForm<T> : Form where T : PresetBase, new()
|
||||
{
|
||||
private ServiceBase<T> _service;
|
||||
private List<Button> _buttons = new List<Button>();
|
||||
private bool _shown = false;
|
||||
|
||||
private static Dictionary<ServiceBase<T>, QuickAccessForm<T>> QuickAccessForms = new();
|
||||
|
||||
public static void ToggleQuickAccessForm(ServiceBase<T> service)
|
||||
{
|
||||
if (!QuickAccessForms.TryGetValue(service, out var form) || form.IsDisposed)
|
||||
{
|
||||
form = new QuickAccessForm<T>(service);
|
||||
QuickAccessForms.Add(service, form);
|
||||
}
|
||||
|
||||
|
||||
if (!form.Visible)
|
||||
{
|
||||
form.Show();
|
||||
form.Activate();
|
||||
}
|
||||
else
|
||||
{
|
||||
form.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public QuickAccessForm(ServiceBase<T> service)
|
||||
{
|
||||
_service = service;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
lblTitle.Text = $"Quick Access - {service.ServiceName}";
|
||||
}
|
||||
|
||||
private void GenerateComponents()
|
||||
{
|
||||
var presets = _service.GetPresets().Where(p => p.ShowInQuickAccess);
|
||||
|
||||
flowPanel.Controls.Clear();
|
||||
_buttons.Clear();
|
||||
|
||||
if (presets.Any())
|
||||
{
|
||||
foreach (var preset in presets)
|
||||
{
|
||||
CreateGroup(preset);
|
||||
}
|
||||
Height = Height - flowPanel.Height + (_buttons.Count() * 46) + 8;
|
||||
|
||||
return;
|
||||
}
|
||||
var panel = new Panel();
|
||||
panel.Width = flowPanel.Width - 4;
|
||||
panel.Height = 40;
|
||||
|
||||
var label = new Label();
|
||||
label.Text = "No Quick Access presets configured";
|
||||
label.Parent = panel;
|
||||
label.AutoSize = true;
|
||||
label.ForeColor = Color.White;
|
||||
|
||||
panel.Parent = flowPanel;
|
||||
|
||||
Height = Height - flowPanel.Height + 36;
|
||||
}
|
||||
|
||||
private Button CreateGroup(PresetBase preset)
|
||||
{
|
||||
var panel = new Panel();
|
||||
panel.Width = flowPanel.Width - 4;
|
||||
panel.Height = 40;
|
||||
|
||||
var button = new Button();
|
||||
button.Text = preset.name;
|
||||
button.Tag = preset;
|
||||
button.Font = new Font(button.Font, FontStyle.Bold);
|
||||
button.Parent = panel;
|
||||
button.Width = panel.Width - 4;
|
||||
button.Height = panel.Height - 4;
|
||||
var margin = button.Margin;
|
||||
margin.Top = 0;
|
||||
margin.Bottom = 0;
|
||||
button.Margin = margin;
|
||||
button.ForeColor = Color.White;
|
||||
button.FlatStyle = FlatStyle.Flat;
|
||||
button.FlatAppearance.BorderColor = Color.DarkGray;
|
||||
button.FlatAppearance.MouseOverBackColor = Color.DimGray;
|
||||
button.Click += Button_Click;
|
||||
|
||||
panel.Parent = flowPanel;
|
||||
|
||||
_buttons.Add(button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, EventArgs e)
|
||||
{
|
||||
var preset = (T)(sender as Button).Tag;
|
||||
|
||||
if (NWin32.NativeMethods.GetAsyncKeyState(NWin32.NativeConstants.VK_SHIFT) < 0)
|
||||
{
|
||||
preset.ShowInQuickAccess = false;
|
||||
|
||||
GenerateComponents();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Hide();
|
||||
|
||||
var _ = _service.ApplyPreset(preset);
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void HandleKey(Keys key)
|
||||
{
|
||||
if (key == Keys.Escape)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
ResetAutoHide();
|
||||
|
||||
if (!_buttons.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ActiveControl is Button button))
|
||||
{
|
||||
_buttons.First().Focus();
|
||||
return;
|
||||
}
|
||||
var diff = 0;
|
||||
|
||||
if (key == Keys.Up)
|
||||
{
|
||||
diff = -1;
|
||||
}
|
||||
else if (key == Keys.Down)
|
||||
{
|
||||
diff = 1;
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var index = _buttons.IndexOf(button);
|
||||
index += diff;
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
index = _buttons.Count - 1;
|
||||
}
|
||||
else if (index == _buttons.Count)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
button = _buttons[index];
|
||||
|
||||
button.Focus();
|
||||
}
|
||||
|
||||
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
HandleKey(e.KeyCode);
|
||||
}
|
||||
|
||||
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
GenerateComponents();
|
||||
ResetAutoHide();
|
||||
|
||||
_shown = true;
|
||||
}
|
||||
|
||||
private void LgGameBar_Deactivate(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void tmrHide_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void LgGameBar_Activated(object sender, EventArgs e)
|
||||
{
|
||||
if (_shown)
|
||||
{
|
||||
GenerateComponents();
|
||||
}
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void LgGameBar_Move(object sender, EventArgs e)
|
||||
{
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void ResetAutoHide()
|
||||
{
|
||||
tmrHide.Enabled = false;
|
||||
tmrHide.Enabled = true;
|
||||
}
|
||||
|
||||
private void QuickAccessForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
|
||||
{
|
||||
HandleKey(e.KeyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="tmrHide.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -0,0 +1,171 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
abstract class ServiceBase<T> where T : PresetBase, new()
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public event EventHandler<T> AfterApplyPreset;
|
||||
|
||||
public abstract string ServiceName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
protected string _dataPath;
|
||||
protected string _presetsBaseFilename;
|
||||
protected string _presetsFilename;
|
||||
protected string _presetsBackupFilename;
|
||||
protected bool _initialized = false;
|
||||
protected List<T> _presets;
|
||||
protected T _lastAppliedPreset;
|
||||
protected string _loadPresetsError;
|
||||
|
||||
private List<JsonConverter> _jsonConverters;
|
||||
|
||||
public ServiceBase(string dataPath, string presetsBaseFilename)
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
_presetsBaseFilename = presetsBaseFilename;
|
||||
_jsonConverters = new List<JsonConverter>();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~ServiceBase()
|
||||
{
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
public List<T> GetPresets()
|
||||
{
|
||||
return _presets;
|
||||
}
|
||||
|
||||
public T GetPresetByIdOrName(string idOrName)
|
||||
{
|
||||
var preset = _presets.FirstOrDefault(p => p.name != null && p.name.Equals(idOrName, StringComparison.OrdinalIgnoreCase));
|
||||
if (preset == null && int.TryParse(idOrName, out var id))
|
||||
{
|
||||
preset = _presets.FirstOrDefault(p => p.id == id);
|
||||
}
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
public abstract bool ApplyPreset(T preset);
|
||||
|
||||
public T GetLastAppliedPreset()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
public virtual T CreateNewPreset()
|
||||
{
|
||||
var preset = new T();
|
||||
var name = "New preset";
|
||||
string fullname;
|
||||
var number = 1;
|
||||
do
|
||||
{
|
||||
fullname = $"{name} ({number})";
|
||||
number++;
|
||||
} while (_presets.Any(x => x.name.Equals(fullname)));
|
||||
|
||||
preset.name = fullname;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
protected virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Uninitialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void PresetApplied()
|
||||
{
|
||||
AfterApplyPreset?.Invoke(this, _lastAppliedPreset);
|
||||
}
|
||||
|
||||
protected void LoadPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, _presetsBaseFilename);
|
||||
_presetsBackupFilename = Path.Combine(_dataPath, $"{_presetsBaseFilename}.backup");
|
||||
|
||||
try
|
||||
{
|
||||
var presetsExists = File.Exists(_presetsFilename);
|
||||
if (presetsExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
// Hack to convert incorrect triggers
|
||||
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
|
||||
|
||||
_presets = JsonConvert.DeserializeObject<List<T>>(json, _jsonConverters.ToArray());
|
||||
|
||||
if (_presets != null)
|
||||
{
|
||||
if (File.Exists(_presetsBackupFilename))
|
||||
{
|
||||
File.Delete(_presetsBackupFilename);
|
||||
}
|
||||
File.Copy(_presetsFilename, _presetsBackupFilename);
|
||||
}
|
||||
}
|
||||
catch (Exception ex1)
|
||||
{
|
||||
Logger.Error($"Error while loading presets, reverting to default presets: {ex1.Message}");
|
||||
_loadPresetsError = ex1.Message;
|
||||
}
|
||||
}
|
||||
if (!presetsExists || _presets == null)
|
||||
{
|
||||
Logger.Debug("Reverting to default presets");
|
||||
try
|
||||
{
|
||||
_presets = GetDefaultPresets();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Error loading default presets, reverting to empty list: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"General error while loading presets, reverting to empty list: {ex.Message}");
|
||||
_loadPresetsError = ex.Message;
|
||||
}
|
||||
if (_presets == null)
|
||||
{
|
||||
_presets = new List<T>();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<T> GetDefaultPresets();
|
||||
|
||||
protected void AddJsonConverter(JsonConverter jsonConverter)
|
||||
{
|
||||
_jsonConverters.Add(jsonConverter);
|
||||
}
|
||||
|
||||
public void ToggleQuickAccessForm()
|
||||
{
|
||||
QuickAccessForm<T>.ToggleQuickAccessForm(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
class ServiceManager
|
||||
{
|
||||
public static ServiceManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new ServiceManager();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
private static ServiceManager _instance { get; set; }
|
||||
|
||||
//private List<ServiceBase<PresetBase>> _services = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using ColorControl.Services.Common;
|
||||
using NWin32;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ColorControl.Services.GameLauncher
|
||||
{
|
||||
public enum GameStepType
|
||||
{
|
||||
[Description("Pre-launch steps")]
|
||||
PreLaunch = 0,
|
||||
[Description("Post-launch steps")]
|
||||
PostLaunch = 1,
|
||||
[Description("Finalize steps")]
|
||||
Finalize = 2,
|
||||
}
|
||||
|
||||
public enum GamePriorityClass
|
||||
{
|
||||
Idle = NativeConstants.IDLE_PRIORITY_CLASS,
|
||||
BelowNormal = NativeConstants.BELOW_NORMAL_PRIORITY_CLASS,
|
||||
Normal = NativeConstants.NORMAL_PRIORITY_CLASS,
|
||||
AboveNormal = NativeConstants.ABOVE_NORMAL_PRIORITY_CLASS,
|
||||
High = NativeConstants.HIGH_PRIORITY_CLASS,
|
||||
RealTime = NativeConstants.REALTIME_PRIORITY_CLASS
|
||||
}
|
||||
|
||||
class GamePreset : PresetBase
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Parameters { get; set; }
|
||||
public bool RunAsAdministrator { get; set; }
|
||||
public List<string> PreLaunchSteps { get; set; }
|
||||
public List<string> PostLaunchSteps { get; set; }
|
||||
public List<string> FinalizeSteps { get; set; }
|
||||
public uint ProcessAffinityMask { get; set; }
|
||||
public uint ProcessPriorityClass { get; set; }
|
||||
|
||||
public GamePreset() : base()
|
||||
{
|
||||
PreLaunchSteps = new List<string>();
|
||||
PostLaunchSteps = new List<string>();
|
||||
FinalizeSteps = new List<string>();
|
||||
}
|
||||
|
||||
public GamePreset(GamePreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
name = preset.name;
|
||||
Path = preset.Path;
|
||||
Parameters = preset.Parameters;
|
||||
RunAsAdministrator = preset.RunAsAdministrator;
|
||||
PreLaunchSteps.AddRange(preset.PreLaunchSteps);
|
||||
PostLaunchSteps.AddRange(preset.PostLaunchSteps);
|
||||
FinalizeSteps.AddRange(preset.FinalizeSteps);
|
||||
}
|
||||
|
||||
public GamePreset Clone()
|
||||
{
|
||||
var preset = new GamePreset(this);
|
||||
|
||||
preset.name += " (copy)";
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300", "Post-launch steps|300", "Finalize steps|300" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
values.Add(Path);
|
||||
values.Add(Parameters);
|
||||
|
||||
values.Add(string.Join(", ", PreLaunchSteps));
|
||||
values.Add(string.Join(", ", PostLaunchSteps));
|
||||
values.Add(string.Join(", ", FinalizeSteps));
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ColorControl.Services.GameLauncher
|
||||
{
|
||||
class GameService : ServiceBase<GamePreset>
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public override string ServiceName => "Game";
|
||||
|
||||
private Func<string, string[], bool> _externalServiceHandler;
|
||||
|
||||
public GameService(string dataPath, Func<string, string[], bool> externalServiceHandler = null) : base(dataPath, "GamePresets.json")
|
||||
{
|
||||
LoadPresets();
|
||||
_externalServiceHandler = externalServiceHandler;
|
||||
}
|
||||
|
||||
public static bool ExecutePresetAsync(string idOrName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = new GameService(Program.DataDir);
|
||||
|
||||
var result = service.ApplyPreset(idOrName);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Console.WriteLine("Preset not found or error while executing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error executing preset: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override List<GamePreset> GetDefaultPresets()
|
||||
{
|
||||
return new List<GamePreset>();
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
Utils.WriteObject(_presetsFilename, _presets);
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
public bool ApplyPreset(string idOrName)
|
||||
{
|
||||
var preset = GetPresetByIdOrName(idOrName);
|
||||
if (preset != null)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ApplyPreset(GamePreset preset)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
}
|
||||
|
||||
public bool ApplyPreset(GamePreset preset, AppContext appContext)
|
||||
{
|
||||
var result = true;
|
||||
|
||||
ExecuteSteps(preset.PreLaunchSteps);
|
||||
|
||||
Process process = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(preset.Path))
|
||||
{
|
||||
process = Utils.StartProcess(preset.Path, preset.Parameters, setWorkingDir: true, elevate: preset.RunAsAdministrator, affinityMask: preset.ProcessAffinityMask, priorityClass: preset.ProcessPriorityClass);
|
||||
}
|
||||
|
||||
ExecuteSteps(preset.PostLaunchSteps);
|
||||
|
||||
if (process != null && preset.FinalizeSteps?.Any() == true)
|
||||
{
|
||||
var _ = ExecuteFinalizationStepsAsync(process, preset.FinalizeSteps);
|
||||
}
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task ExecuteFinalizationStepsAsync(Process process, List<string> finalizeSteps)
|
||||
{
|
||||
await process.WaitForExitAsync();
|
||||
|
||||
ExecuteSteps(finalizeSteps);
|
||||
}
|
||||
|
||||
private void ExecuteSteps(List<string> steps)
|
||||
{
|
||||
if (steps?.Any() != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var step in steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
|
||||
var delay = 0;
|
||||
var key = step;
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
delay = Utils.ParseInt(keySpec[1]);
|
||||
if (delay > 0)
|
||||
{
|
||||
key = keySpec[0];
|
||||
}
|
||||
}
|
||||
|
||||
var index = key.IndexOf("(");
|
||||
string[] parameters = null;
|
||||
if (index > -1)
|
||||
{
|
||||
var keyValue = key.Split('(');
|
||||
key = keyValue[0];
|
||||
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Trim().Split(';');
|
||||
}
|
||||
|
||||
var handled = false;
|
||||
|
||||
if (_externalServiceHandler != null && parameters != null)
|
||||
{
|
||||
handled = _externalServiceHandler(key, parameters);
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
}
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
Utils.WaitForTask(Task.Delay(delay));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+139
@@ -0,0 +1,139 @@
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class LgGameBar
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.lblTvName = new System.Windows.Forms.Label();
|
||||
this.tmrHide = new System.Windows.Forms.Timer(this.components);
|
||||
this.lblProcessName = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// flowPanel
|
||||
//
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.Location = new System.Drawing.Point(14, 47);
|
||||
this.flowPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(270, 325);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
|
||||
this.flowPanel.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.flowPanel_PreviewKeyDown);
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnClose.FlatAppearance.BorderColor = System.Drawing.Color.DarkGray;
|
||||
this.btnClose.FlatAppearance.BorderSize = 0;
|
||||
this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnClose.ForeColor = System.Drawing.Color.White;
|
||||
this.btnClose.Location = new System.Drawing.Point(280, 0);
|
||||
this.btnClose.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Size = new System.Drawing.Size(16, 27);
|
||||
this.btnClose.TabIndex = 1;
|
||||
this.btnClose.TabStop = false;
|
||||
this.btnClose.Text = "x";
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// lblTvName
|
||||
//
|
||||
this.lblTvName.AutoSize = true;
|
||||
this.lblTvName.ForeColor = System.Drawing.Color.White;
|
||||
this.lblTvName.Location = new System.Drawing.Point(14, 0);
|
||||
this.lblTvName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lblTvName.Name = "lblTvName";
|
||||
this.lblTvName.Size = new System.Drawing.Size(25, 15);
|
||||
this.lblTvName.TabIndex = 0;
|
||||
this.lblTvName.Text = " ";
|
||||
//
|
||||
// tmrHide
|
||||
//
|
||||
this.tmrHide.Interval = 5000;
|
||||
this.tmrHide.Tick += new System.EventHandler(this.tmrHide_Tick);
|
||||
//
|
||||
// lblProcessName
|
||||
//
|
||||
this.lblProcessName.AutoSize = true;
|
||||
this.lblProcessName.ForeColor = System.Drawing.Color.White;
|
||||
this.lblProcessName.Location = new System.Drawing.Point(14, 23);
|
||||
this.lblProcessName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lblProcessName.Name = "lblProcessName";
|
||||
this.lblProcessName.Size = new System.Drawing.Size(25, 15);
|
||||
this.lblProcessName.TabIndex = 2;
|
||||
this.lblProcessName.Text = " ";
|
||||
//
|
||||
// LgGameBar
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Black;
|
||||
this.ClientSize = new System.Drawing.Size(298, 387);
|
||||
this.Controls.Add(this.lblProcessName);
|
||||
this.Controls.Add(this.lblTvName);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.flowPanel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.KeyPreview = true;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "LgGameBar";
|
||||
this.Opacity = 0.7D;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Text = "RemoteControlForm";
|
||||
this.TopMost = true;
|
||||
this.Activated += new System.EventHandler(this.LgGameBar_Activated);
|
||||
this.Deactivate += new System.EventHandler(this.LgGameBar_Deactivate);
|
||||
this.Load += new System.EventHandler(this.RemoteControlForm_Load);
|
||||
this.Shown += new System.EventHandler(this.RemoteControlForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyDown);
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp);
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
this.Move += new System.EventHandler(this.LgGameBar_Move);
|
||||
this.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.RemoteControlForm_PreviewKeyDown);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
private System.Windows.Forms.Label lblTvName;
|
||||
private System.Windows.Forms.Timer tmrHide;
|
||||
private System.Windows.Forms.Label lblProcessName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
using ColorControl.Common;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class LgGameBar : Form
|
||||
{
|
||||
private class TbSetting
|
||||
{
|
||||
public TrackBar TrackBar { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Type EnumType { get; set; }
|
||||
public string PropertyName { get; set; }
|
||||
}
|
||||
|
||||
private LgService _lgService;
|
||||
private LgDevice _lgDevice;
|
||||
private ToolTip _toolTip;
|
||||
private bool _settingChanged = false;
|
||||
private List<TrackBar> _trackBars = new List<TrackBar>();
|
||||
private List<string> _lastActions = new List<string>();
|
||||
|
||||
public LgGameBar(LgService lgService)
|
||||
{
|
||||
_lgService = lgService;
|
||||
|
||||
_lgDevice = _lgService.SelectedDevice;
|
||||
|
||||
_lgDevice.PictureSettingsChangedEvent += LgDevice_PictureSettingsChangedEvent;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
_toolTip = new ToolTip();
|
||||
}
|
||||
|
||||
private void GenerateComponents()
|
||||
{
|
||||
var actions = _lgDevice.GetActionsForGameBar();
|
||||
var actionNames = actions.Select(a => a.Name).ToList();
|
||||
|
||||
if (_lastActions.Count == actions.Count && _lastActions.All(a => actionNames.Contains(a)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
flowPanel.Controls.Clear();
|
||||
_trackBars.Clear();
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
CreateGroup(action.Title, action.Name, (int)action.MinValue, (int)action.MaxValue, action.EnumType);
|
||||
}
|
||||
|
||||
_lastActions.Clear();
|
||||
_lastActions.AddRange(actionNames);
|
||||
|
||||
Width = 28 + (_trackBars.Count() * 56);
|
||||
}
|
||||
|
||||
private TrackBar CreateGroup(string title, string settingName = null, int min = 0, int max = 100, Type enumType = null)
|
||||
{
|
||||
if (settingName == null)
|
||||
{
|
||||
settingName = title.ToLowerInvariant();
|
||||
}
|
||||
|
||||
var panel = new Panel();
|
||||
panel.Width = 50;
|
||||
panel.Height = flowPanel.Height;
|
||||
var label = new Label();
|
||||
label.Text = title;
|
||||
label.ForeColor = Color.White;
|
||||
label.Parent = panel;
|
||||
|
||||
if (min == 0 && max == 0 && enumType != null)
|
||||
{
|
||||
max = Enum.GetValues(enumType).Length - 1;
|
||||
}
|
||||
|
||||
var trackBar = CreateTrackBar(min, max);
|
||||
trackBar.Top = label.Height;
|
||||
trackBar.Height = panel.Height - trackBar.Top - 4;
|
||||
trackBar.Parent = panel;
|
||||
trackBar.Scroll += tbTrackBar_Scroll;
|
||||
var margin = trackBar.Margin;
|
||||
margin.Top = 0;
|
||||
margin.Bottom = 0;
|
||||
trackBar.Margin = margin;
|
||||
trackBar.ValueChanged += TrackBar_ValueChanged;
|
||||
|
||||
var setting = new TbSetting
|
||||
{
|
||||
TrackBar = trackBar,
|
||||
Name = settingName,
|
||||
EnumType = enumType,
|
||||
PropertyName = Utils.FirstCharUpperCase(settingName)
|
||||
};
|
||||
|
||||
trackBar.Tag = setting;
|
||||
|
||||
panel.Parent = flowPanel;
|
||||
|
||||
_trackBars.Add(trackBar);
|
||||
|
||||
return trackBar;
|
||||
}
|
||||
|
||||
private void TrackBar_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
//var trackBar = sender as TrackBar;
|
||||
|
||||
//if (_toolTip != null)
|
||||
//{
|
||||
// _toolTip.SetToolTip(trackBar, trackBar.Value.ToString());
|
||||
//}
|
||||
}
|
||||
|
||||
private TrackBar CreateTrackBar(int min, int max)
|
||||
{
|
||||
var trackBar = new TrackBar();
|
||||
trackBar.BackColor = Color.Black;
|
||||
trackBar.Orientation = Orientation.Vertical;
|
||||
trackBar.TickFrequency = (max >= 50) ? 5 : 1;
|
||||
trackBar.TickStyle = TickStyle.Both;
|
||||
trackBar.Minimum = min;
|
||||
trackBar.Maximum = max;
|
||||
|
||||
return trackBar;
|
||||
}
|
||||
|
||||
private void LgDevice_PictureSettingsChangedEvent(object sender, EventArgs e)
|
||||
{
|
||||
if (_settingChanged)
|
||||
{
|
||||
_settingChanged = false;
|
||||
return;
|
||||
}
|
||||
BeginInvoke(new Action(UpdateValues));
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
var dynType = _lgDevice.PictureSettings.GetType();
|
||||
var actions = _lgDevice.GetInvokableActions();
|
||||
foreach (var trackbar in _trackBars.Where(t => !t.Focused))
|
||||
{
|
||||
var setting = (TbSetting)trackbar.Tag;
|
||||
|
||||
if (setting.PropertyName == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var prop = dynType.GetProperty(setting.PropertyName);
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
var value = prop.GetValue(_lgDevice.PictureSettings);
|
||||
|
||||
if (value is int intValue)
|
||||
{
|
||||
trackbar.Value = intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var action = actions.FirstOrDefault(a => a.Name == setting.Name);
|
||||
if (action != null)
|
||||
{
|
||||
trackbar.Value = action.CurrentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
Left = _lgService.Config.GameBarLeft;
|
||||
Top = _lgService.Config.GameBarTop;
|
||||
}
|
||||
|
||||
private void ButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
if (e is MouseEventArgs)
|
||||
{
|
||||
var preset = (LgPreset)(sender as Button).Tag;
|
||||
|
||||
var _ = _lgService.ApplyPreset(preset);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
NativeMethods.ReleaseCapture();
|
||||
var intPtr = new UIntPtr(2);
|
||||
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
//var key = e.KeyCode;
|
||||
//HandleKey(key);
|
||||
}
|
||||
|
||||
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
|
||||
{
|
||||
//HandleKey(e.KeyCode);
|
||||
}
|
||||
|
||||
private void HandleKey(Keys key)
|
||||
{
|
||||
var cvt = new KeysConverter();
|
||||
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
|
||||
}
|
||||
|
||||
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
|
||||
{
|
||||
//HandleKey(e.KeyCode);
|
||||
}
|
||||
|
||||
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.F1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleKey(e.KeyCode);
|
||||
}
|
||||
|
||||
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
GenerateComponents();
|
||||
UpdateInfo();
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void UpdateInfo()
|
||||
{
|
||||
lblTvName.Text = $"Device: {_lgDevice?.Name ?? "No tv selected"}";
|
||||
lblProcessName.Text = $"Current process: {_lgService.MonitorContext?.ForegroundProcess?.ProcessName ?? _lgService.MonitorContext?.LastFullScreenProcessName ?? "No full screen process"}";
|
||||
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private void LgGameBar_Deactivate(object sender, EventArgs e)
|
||||
{
|
||||
_lgService.Config.GameBarLeft = Left;
|
||||
_lgService.Config.GameBarTop = Top;
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void tmrHide_Tick(object sender, EventArgs e)
|
||||
{
|
||||
_lgService.Config.GameBarLeft = Left;
|
||||
_lgService.Config.GameBarTop = Top;
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void tbTrackBar_Scroll(object sender, EventArgs e)
|
||||
{
|
||||
_settingChanged = true;
|
||||
ResetAutoHide();
|
||||
|
||||
var trackBar = sender as TrackBar;
|
||||
var setting = (TbSetting)trackBar.Tag;
|
||||
|
||||
var value = string.Empty;
|
||||
|
||||
if (setting.EnumType == null)
|
||||
{
|
||||
value = trackBar.Value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
var enumValue = Enum.Parse(setting.EnumType, trackBar.Value.ToString());
|
||||
value = enumValue.ToString();
|
||||
}
|
||||
|
||||
_lgDevice.SetSystemSettings(setting.Name, value).ConfigureAwait(false);
|
||||
|
||||
if (_toolTip != null)
|
||||
{
|
||||
_toolTip.Show(value, trackBar, 10, 50, 1500);
|
||||
}
|
||||
}
|
||||
|
||||
private void LgGameBar_Activated(object sender, EventArgs e)
|
||||
{
|
||||
GenerateComponents();
|
||||
UpdateInfo();
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void LgGameBar_Move(object sender, EventArgs e)
|
||||
{
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void ResetAutoHide()
|
||||
{
|
||||
tmrHide.Enabled = false;
|
||||
tmrHide.Enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="tmrHide.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -0,0 +1,117 @@
|
||||
using ColorControl.Services.Common;
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
class LgPreset : PresetBase
|
||||
{
|
||||
public static List<LgApp> LgApps { get; set; }
|
||||
public static List<LgDevice> LgDevices { get; set; }
|
||||
public string appId { get; set; }
|
||||
public List<string> steps { get; set; }
|
||||
public string DeviceMacAddress { get; set; }
|
||||
public dynamic AppParams { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int X { get; set; }
|
||||
[JsonIgnore]
|
||||
public int Y { get; set; }
|
||||
|
||||
public LgPreset() : base()
|
||||
{
|
||||
steps = new List<string>();
|
||||
}
|
||||
|
||||
public LgPreset(LgPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
name = preset.name + " (copy)";
|
||||
appId = preset.appId;
|
||||
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
steps.Add(step);
|
||||
}
|
||||
}
|
||||
|
||||
public LgPreset(string name, string appId, IEnumerable<string> steps = null, dynamic appParams = null) : this()
|
||||
{
|
||||
this.name = name;
|
||||
this.appId = appId;
|
||||
if (steps != null)
|
||||
{
|
||||
this.steps.AddRange(steps);
|
||||
}
|
||||
AppParams = appParams;
|
||||
}
|
||||
|
||||
public LgPreset Clone()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static List<string> GetColumnNames()
|
||||
{
|
||||
return new List<string>() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut", "Trigger" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var deviceString = "Global";
|
||||
if (!string.IsNullOrEmpty(DeviceMacAddress))
|
||||
{
|
||||
if (LgDevices != null)
|
||||
{
|
||||
var device = LgDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress));
|
||||
if (device != null)
|
||||
{
|
||||
deviceString = device.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceString = "Unknown: device not found";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceString = DeviceMacAddress;
|
||||
}
|
||||
}
|
||||
values.Add(deviceString);
|
||||
|
||||
var app = appId;
|
||||
if (LgApps != null)
|
||||
{
|
||||
var item = LgApps.FirstOrDefault(x => x.appId.Equals(appId));
|
||||
if (item != null)
|
||||
{
|
||||
app = item.title + " (" + appId + ")";
|
||||
}
|
||||
}
|
||||
values.Add(app);
|
||||
values.Add(GetStepsDisplay());
|
||||
values.Add(shortcut);
|
||||
|
||||
if (Triggers.Any())
|
||||
{
|
||||
values.Add(Triggers.First().ToString());
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public string GetStepsDisplay()
|
||||
{
|
||||
return steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,958 @@
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Forms;
|
||||
using ColorControl.Services.Common;
|
||||
using ColorControl.Svc;
|
||||
using LgTv;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
class LgService : ServiceBase<LgPreset>
|
||||
{
|
||||
internal enum PowerOnOffState
|
||||
{
|
||||
StartUp = 1,
|
||||
Resume = 2,
|
||||
ScreenSaver = 3,
|
||||
ShutDown = 4,
|
||||
StandBy = 5
|
||||
}
|
||||
|
||||
internal enum WindowsPowerSetting
|
||||
{
|
||||
Off = 0,
|
||||
On = 1,
|
||||
Dimmed = 2
|
||||
}
|
||||
|
||||
internal class ProcessMonitorContext
|
||||
{
|
||||
public List<LgDevice> Devices { get; set; }
|
||||
public bool WasFullScreen { get; set; }
|
||||
public Process[] StartedProcesses { get; set; }
|
||||
public Process[] StoppedProcesses { get; set; }
|
||||
public Process[] RunningProcesses { get; set; }
|
||||
public bool IsNotificationDisabled { get; set; }
|
||||
public Process ForegroundProcess { get; set; }
|
||||
public bool ForegroundProcessIsFullScreen { get; set; }
|
||||
public string LastFullScreenProcessName = string.Empty;
|
||||
}
|
||||
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static string LgListAppsJson = "listApps.json";
|
||||
|
||||
public override string ServiceName => "LG";
|
||||
|
||||
public List<LgDevice> Devices { get; private set; }
|
||||
public LgDevice SelectedDevice
|
||||
{
|
||||
get { return _selectedDevice; }
|
||||
set
|
||||
{
|
||||
_selectedDevice = value;
|
||||
Config.PreferredMacAddress = _selectedDevice != null ? _selectedDevice.MacAddress : null;
|
||||
|
||||
SelectedDeviceChangedEvent?.Invoke(_selectedDevice, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
public event EventHandler SelectedDeviceChangedEvent;
|
||||
|
||||
public LgServiceConfig Config { get; private set; }
|
||||
public ProcessMonitorContext MonitorContext { get; private set; }
|
||||
|
||||
private string _configFilename;
|
||||
private bool _allowPowerOn;
|
||||
private LgDevice _selectedDevice;
|
||||
private string _rcButtonsFilename;
|
||||
private List<LgPreset> _remoteControlButtons;
|
||||
private List<LgApp> _lgApps = new List<LgApp>();
|
||||
|
||||
private bool _poweredOffByScreenSaver;
|
||||
private int _poweredOffByScreenSaverProcessId;
|
||||
private Task _monitorTask;
|
||||
private int _monitorTaskCounter;
|
||||
private LgPreset _lastTriggeredPreset;
|
||||
private RestartDetector _restartDetector;
|
||||
|
||||
public LgService(string dataPath, bool allowPowerOn) : base(dataPath, "LgPresets.json")
|
||||
{
|
||||
_allowPowerOn = allowPowerOn;
|
||||
|
||||
LgPreset.LgApps = _lgApps;
|
||||
|
||||
_restartDetector = new RestartDetector();
|
||||
|
||||
LoadConfig();
|
||||
LoadPresets();
|
||||
LoadRemoteControlButtons();
|
||||
}
|
||||
|
||||
~LgService()
|
||||
{
|
||||
GlobalSave();
|
||||
}
|
||||
|
||||
public static async Task<bool> ExecutePresetAsync(string presetName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lgService = new LgService(Program.DataDir, true);
|
||||
await lgService.RefreshDevices(afterStartUp: true);
|
||||
|
||||
var result = await lgService.ApplyPreset(presetName);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Console.WriteLine("Preset not found or error while executing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error executing preset: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SaveConfig();
|
||||
SavePresets();
|
||||
SaveRemoteControlButtons();
|
||||
|
||||
SendConfigToService();
|
||||
}
|
||||
|
||||
protected override List<LgPreset> GetDefaultPresets()
|
||||
{
|
||||
List<LgPreset> presets = null;
|
||||
|
||||
var defaultPresetsFileName = Path.Combine(Directory.GetCurrentDirectory(), "LgPresets.json");
|
||||
if (File.Exists(defaultPresetsFileName))
|
||||
{
|
||||
var json = File.ReadAllText(defaultPresetsFileName);
|
||||
|
||||
presets = JsonConvert.DeserializeObject<List<LgPreset>>(json);
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
{
|
||||
_configFilename = Path.Combine(_dataPath, "LgConfig.json");
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configFilename))
|
||||
{
|
||||
Config = JsonConvert.DeserializeObject<LgServiceConfig>(File.ReadAllText(_configFilename));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new LgServiceConfig();
|
||||
|
||||
LgPreset.LgDevices = Config.Devices;
|
||||
}
|
||||
|
||||
private void LoadRemoteControlButtons()
|
||||
{
|
||||
var defaultButtons = GenerateDefaultRemoteControlButtons();
|
||||
|
||||
_rcButtonsFilename = Path.Combine(_dataPath, "LgRemoteControlButtons.json");
|
||||
//if (File.Exists(_rcButtonsFilename))
|
||||
//{
|
||||
// var json = File.ReadAllText(_rcButtonsFilename);
|
||||
|
||||
// _remoteControlButtons = _JsonDeserializer.Deserialize<List<LgPreset>>(json);
|
||||
|
||||
// var missingButtons = defaultButtons.Where(b => !_remoteControlButtons.Any(x => x.name.Equals(b.name)));
|
||||
// _remoteControlButtons.AddRange(missingButtons);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
_remoteControlButtons = defaultButtons;
|
||||
}
|
||||
}
|
||||
|
||||
public List<LgPreset> GetRemoteControlButtons()
|
||||
{
|
||||
return _remoteControlButtons;
|
||||
}
|
||||
|
||||
private List<LgPreset> GenerateDefaultRemoteControlButtons()
|
||||
{
|
||||
var list = new List<LgPreset>();
|
||||
|
||||
for (var i = 1; i < 11; i++)
|
||||
{
|
||||
var button = i < 10 ? i : 0;
|
||||
list.Add(GeneratePreset(button.ToString()));
|
||||
}
|
||||
|
||||
list.Add(GeneratePreset("Power", step: "POWER"));
|
||||
list.Add(GeneratePreset("Wol", step: "WOL"));
|
||||
list.Add(GeneratePreset("Home", appId: "com.webos.app.home", key: Keys.H));
|
||||
list.Add(GeneratePreset("Settings", appId: "com.palm.app.settings", key: Keys.S));
|
||||
list.Add(GeneratePreset("TV Guide", appId: "com.webos.app.livemenu"));
|
||||
list.Add(GeneratePreset("List", step: "LIST"));
|
||||
list.Add(GeneratePreset("SAP", step: "SAP"));
|
||||
|
||||
list.Add(GeneratePreset("Vol +", step: "VOLUMEUP", key: Keys.VolumeUp));
|
||||
list.Add(GeneratePreset("Vol -", step: "VOLUMEDOWN", key: Keys.VolumeDown));
|
||||
list.Add(GeneratePreset("Mute", step: "MUTE", key: Keys.VolumeMute));
|
||||
list.Add(GeneratePreset("Channel +", step: "CHANNELUP", key: Keys.MediaNextTrack));
|
||||
list.Add(GeneratePreset("Channel -", step: "CHANNELDOWN", key: Keys.MediaPreviousTrack));
|
||||
|
||||
list.Add(GeneratePreset("Up", step: "UP", key: Keys.Up));
|
||||
list.Add(GeneratePreset("Down", step: "DOWN", key: Keys.Down));
|
||||
list.Add(GeneratePreset("Left", step: "LEFT", key: Keys.Left));
|
||||
list.Add(GeneratePreset("Right", step: "RIGHT", key: Keys.Right));
|
||||
|
||||
list.Add(GeneratePreset("Enter", step: "ENTER", key: Keys.Enter));
|
||||
list.Add(GeneratePreset("Back", step: "BACK", key: Keys.Back));
|
||||
list.Add(GeneratePreset("Exit", step: "EXIT", key: Keys.Escape));
|
||||
|
||||
list.Add(GeneratePreset("Netflix", appId: "netflix"));
|
||||
list.Add(GeneratePreset("Inputs", appId: "com.webos.app.homeconnect"));
|
||||
list.Add(GeneratePreset("Amazon Prime", appId: "amazon"));
|
||||
|
||||
list.Add(GeneratePreset("Red", step: "RED"));
|
||||
list.Add(GeneratePreset("Green", step: "GREEN"));
|
||||
list.Add(GeneratePreset("Yellow", step: "YELLOW"));
|
||||
list.Add(GeneratePreset("Blue", step: "BLUE"));
|
||||
|
||||
list.Add(GeneratePreset("Rakuten TV", appId: "ui30"));
|
||||
list.Add(GeneratePreset("Play", step: "PLAY"));
|
||||
list.Add(GeneratePreset("Pause", step: "PAUSE"));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private LgPreset GeneratePreset(string name, string step = null, string appId = null, Keys key = Keys.None)
|
||||
{
|
||||
var preset = new LgPreset();
|
||||
|
||||
var cvt = new KeysConverter();
|
||||
var shortcut = key != Keys.None ? (string)cvt.ConvertTo(key, typeof(string)) : null;
|
||||
|
||||
preset.name = name;
|
||||
preset.appId = appId;
|
||||
preset.shortcut = shortcut;
|
||||
if (appId == null)
|
||||
{
|
||||
preset.steps.Add(step == null ? name : step);
|
||||
}
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
Utils.WriteObject(_presetsFilename, _presets);
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
Config.PowerOnAfterStartup = Devices?.Any(d => d.PowerOnAfterStartup) ?? false;
|
||||
|
||||
Utils.WriteObject(_configFilename, Config);
|
||||
}
|
||||
|
||||
private void SaveRemoteControlButtons()
|
||||
{
|
||||
Utils.WriteObject(_rcButtonsFilename, _remoteControlButtons);
|
||||
}
|
||||
|
||||
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
|
||||
{
|
||||
Devices = Config.Devices;
|
||||
var customIpAddresses = Devices.Where(d => d.IsCustom).Select(d => d.IpAddress);
|
||||
|
||||
var pnpDevices = await Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
|
||||
var autoDevices = pnpDevices.Where(p => !customIpAddresses.Contains(p.IpAddress)).Select(d => new LgDevice(d.Name, d.IpAddress, d.MacAddress, false)).ToList();
|
||||
var autoIpAddresses = pnpDevices.Select(d => d.IpAddress);
|
||||
|
||||
Devices.RemoveAll(d => !d.IsCustom && !autoIpAddresses.Contains(d.IpAddress));
|
||||
|
||||
var newAutoDevices = autoDevices.Where(ad => ad.IpAddress != null && !Devices.Any(d => d.IpAddress != null && d.IpAddress.Equals(ad.IpAddress)));
|
||||
Devices.AddRange(newAutoDevices);
|
||||
|
||||
if (Devices.Any())
|
||||
{
|
||||
var preferredDevice = Devices.FirstOrDefault(x => x.MacAddress != null && x.MacAddress.Equals(Config.PreferredMacAddress)) ?? Devices[0];
|
||||
|
||||
SelectedDevice = preferredDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedDevice = null;
|
||||
}
|
||||
|
||||
if (afterStartUp && SelectedDevice == null && Config.PowerOnAfterStartup && !string.IsNullOrEmpty(Config.PreferredMacAddress))
|
||||
{
|
||||
Logger.Debug("No device has been found, trying to wake it first...");
|
||||
|
||||
var tempDevice = new LgDevice("Test", string.Empty, Config.PreferredMacAddress);
|
||||
tempDevice.Wake();
|
||||
|
||||
await Task.Delay(4000);
|
||||
await RefreshDevices();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var device in Devices)
|
||||
{
|
||||
device.PowerStateChangedEvent += LgDevice_PowerStateChangedEvent;
|
||||
}
|
||||
|
||||
if (connect && SelectedDevice != null)
|
||||
{
|
||||
if (_allowPowerOn)
|
||||
{
|
||||
WakeAfterStartupOrResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
var _ = SelectedDevice.Connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ApplyPreset(string presetName)
|
||||
{
|
||||
var preset = _presets.FirstOrDefault(p => p.name.Equals(presetName, StringComparison.OrdinalIgnoreCase));
|
||||
if (preset != null)
|
||||
{
|
||||
return await ApplyPreset(preset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ApplyPreset(LgPreset preset)
|
||||
{
|
||||
var task = ApplyPreset(preset, false);
|
||||
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
return task.Result;
|
||||
}
|
||||
|
||||
public async Task<bool> ApplyPreset(LgPreset preset, bool reconnect = false)
|
||||
{
|
||||
var device = GetPresetDevice(preset);
|
||||
|
||||
if (device == null)
|
||||
{
|
||||
Logger.Debug("Cannot apply preset: no device has been selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = await device.ExecutePreset(preset, reconnect, Config);
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public LgDevice GetPresetDevice(LgPreset preset)
|
||||
{
|
||||
var device = string.IsNullOrEmpty(preset.DeviceMacAddress)
|
||||
? SelectedDevice
|
||||
: Devices.FirstOrDefault(d => d.MacAddress?.Equals(preset.DeviceMacAddress, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
return device;
|
||||
}
|
||||
|
||||
public async Task<LgWebOsMouseService> GetMouseAsync()
|
||||
{
|
||||
return await SelectedDevice?.GetMouseAsync();
|
||||
}
|
||||
|
||||
public async Task RefreshApps(bool force = false)
|
||||
{
|
||||
if (SelectedDevice == null)
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no device has been selected");
|
||||
return;
|
||||
}
|
||||
|
||||
var apps = await SelectedDevice.GetApps(force);
|
||||
|
||||
if (apps.Any())
|
||||
{
|
||||
_lgApps.Clear();
|
||||
_lgApps.AddRange(apps);
|
||||
}
|
||||
}
|
||||
|
||||
public List<LgApp> GetApps()
|
||||
{
|
||||
return _lgApps;
|
||||
}
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
{
|
||||
return await SelectedDevice?.PowerOff();
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice()
|
||||
{
|
||||
return SelectedDevice?.Wake() ?? false;
|
||||
}
|
||||
|
||||
internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
|
||||
{
|
||||
var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup ||
|
||||
state == PowerOnOffState.Resume && d.PowerOnAfterResume ||
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerOnAfterScreenSaver);
|
||||
|
||||
PowerOnDevices(wakeDevices, state, checkUserSession);
|
||||
}
|
||||
|
||||
public void InstallEventHandlers()
|
||||
{
|
||||
SystemEvents.PowerModeChanged -= PowerModeChanged;
|
||||
SystemEvents.PowerModeChanged += PowerModeChanged;
|
||||
//SystemEvents.SessionEnded -= SessionEnded;
|
||||
//SystemEvents.SessionEnded += SessionEnded;
|
||||
|
||||
UserSessionInfo.UserSessionSwitch -= SessionSwitched;
|
||||
UserSessionInfo.UserSessionSwitch += SessionSwitched;
|
||||
|
||||
MonitorProcesses();
|
||||
}
|
||||
|
||||
private void SessionSwitched(bool toLocal)
|
||||
{
|
||||
if (toLocal)
|
||||
{
|
||||
if (_poweredOffByScreenSaver)
|
||||
{
|
||||
Logger.Debug("User switched to local and screen was powered off due to screensaver: waking up");
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
WakeAfterStartupOrResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("User switched to local but screen was not powered off by screensaver: NOT waking up");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
var powerOn = e.Mode == PowerModes.Resume;
|
||||
|
||||
Logger.Debug($"PowerModeChanged: {e.Mode}");
|
||||
|
||||
if (powerOn)
|
||||
{
|
||||
WakeAfterStartupOrResume(PowerOnOffState.Resume);
|
||||
return;
|
||||
}
|
||||
|
||||
var devices = Devices.Where(d => d.PowerOffOnStandby);
|
||||
PowerOffDevices(devices, PowerOnOffState.StandBy);
|
||||
}
|
||||
|
||||
private void SessionEnded(object sender, SessionEndedEventArgs e)
|
||||
{
|
||||
//if (e.Reason == SessionEndReasons.SystemShutdown)
|
||||
//{
|
||||
// Logger.Debug($"SessionEnded: {e.Reason}");
|
||||
|
||||
// if (Config.PowerOffOnShutdown)
|
||||
// {
|
||||
// PowerOff();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private void LgDevice_PowerStateChangedEvent(object sender, EventArgs e)
|
||||
{
|
||||
if (!(sender is LgDevice device))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (device.CurrentState == LgDevice.PowerState.Active)
|
||||
{
|
||||
if (Config.SetSelectedDeviceByPowerOn)
|
||||
{
|
||||
SelectedDevice = device;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MonitorProcesses()
|
||||
{
|
||||
var enableMonitoring = ShouldMonitorProcesses();
|
||||
if (enableMonitoring && _monitorTask == null)
|
||||
{
|
||||
_monitorTask = CheckProcesses();
|
||||
}
|
||||
else if (!enableMonitoring && _monitorTask != null)
|
||||
{
|
||||
_monitorTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldMonitorProcesses()
|
||||
{
|
||||
return Devices.Any(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None));
|
||||
}
|
||||
|
||||
public async Task CheckProcesses()
|
||||
{
|
||||
var wasConnected = Devices.Any(d => (d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) && d.IsConnected());
|
||||
_monitorTaskCounter++;
|
||||
var validCounter = _monitorTaskCounter;
|
||||
var lastProcessId = 0;
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
MonitorContext ??= new ProcessMonitorContext();
|
||||
//Process[] lastProcesses = null;
|
||||
|
||||
while (validCounter == _monitorTaskCounter)
|
||||
{
|
||||
await Task.Delay(_poweredOffByScreenSaver ? 500 : 1000);
|
||||
|
||||
try
|
||||
{
|
||||
var applicableDevices = Devices.Where(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver || d.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
|
||||
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
|
||||
|
||||
if (!applicableDevices.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_poweredOffByScreenSaver && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var processes = Process.GetProcesses();
|
||||
|
||||
if (_poweredOffByScreenSaver)
|
||||
{
|
||||
if (processes.Any(p => p.Id == _poweredOffByScreenSaverProcessId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Debug("Screensaver stopped, waking");
|
||||
_poweredOffByScreenSaver = false;
|
||||
_poweredOffByScreenSaverProcessId = 0;
|
||||
lastProcessId = 0;
|
||||
WakeAfterStartupOrResume(PowerOnOffState.ScreenSaver);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var connectedDevices = applicableDevices.Where(d => d.IsConnected()).ToList();
|
||||
|
||||
if (!connectedDevices.Any())
|
||||
{
|
||||
if (wasConnected)
|
||||
{
|
||||
Logger.Debug("Process monitor: TV(s) where connected, but not any longer");
|
||||
wasConnected = false;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var devices = applicableDevices.ToList();
|
||||
foreach (var device in devices)
|
||||
{
|
||||
Logger.Debug($"Process monitor: test connection with {device.Name}...");
|
||||
var test = await device.TestConnection();
|
||||
Logger.Debug("Process monitor: test connection result: " + test);
|
||||
}
|
||||
|
||||
connectedDevices = applicableDevices.Where(d => d.IsConnected()).ToList();
|
||||
|
||||
if (!connectedDevices.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasConnected)
|
||||
{
|
||||
Logger.Debug("Process monitor: TV(s) where not connected, but connection has now been established");
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
if (connectedDevices.Any(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver))
|
||||
{
|
||||
lastProcessId = await HandleScreenSaverProcessAsync(lastProcessId, processes, connectedDevices);
|
||||
}
|
||||
|
||||
MonitorContext.Devices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
|
||||
MonitorContext.RunningProcesses = processes;
|
||||
|
||||
await ExecuteProcessPresets(MonitorContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("CheckProcesses: " + ex.ToLogString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> HandleScreenSaverProcessAsync(int lastProcessId, Process[] processes, List<LgDevice> connectedDevices)
|
||||
{
|
||||
var process = processes.FirstOrDefault(p => p.ProcessName.ToLowerInvariant().EndsWith(".scr"));
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var parent = process.Parent(processes);
|
||||
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
|
||||
try
|
||||
{
|
||||
foreach (var device in connectedDevices.Where(d => d.PowerOffOnScreenSaver))
|
||||
{
|
||||
Logger.Debug($"Screensaver check: test connection with {device.Name}...");
|
||||
var test = await device.TestConnection();
|
||||
Logger.Debug("Screensaver check: test connection result: " + test);
|
||||
if (!test)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Debug($"Screensaver check: powering off tv {device.Name} because of screensaver");
|
||||
try
|
||||
{
|
||||
_poweredOffByScreenSaver = await device.PowerOff(true).WaitAsync(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
// Assume powering off was successful
|
||||
_poweredOffByScreenSaver = true;
|
||||
Logger.Error($"Screensaver check: timeout exception while powering off (probably connection closed)");
|
||||
}
|
||||
catch (Exception pex)
|
||||
{
|
||||
// Assume powering off was successful
|
||||
_poweredOffByScreenSaver = true;
|
||||
Logger.Error($"Screensaver check: exception while powering off (probably connection closed): " + pex.Message);
|
||||
}
|
||||
Logger.Debug($"Screensaver check: powered off tv {device.Name}");
|
||||
}
|
||||
|
||||
_poweredOffByScreenSaverProcessId = process.Id;
|
||||
lastProcessId = 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
|
||||
}
|
||||
}
|
||||
else if (process.Id != lastProcessId)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
|
||||
lastProcessId = process.Id;
|
||||
}
|
||||
|
||||
return lastProcessId;
|
||||
}
|
||||
|
||||
private async Task ExecuteProcessPresets(ProcessMonitorContext context)
|
||||
{
|
||||
var selectedDevice = SelectedDevice;
|
||||
|
||||
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == PresetTriggerType.ProcessSwitch) &&
|
||||
((string.IsNullOrEmpty(p.DeviceMacAddress) && selectedDevice?.TriggersEnabled == true)
|
||||
|| Devices.Any(d => d.TriggersEnabled && p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
|
||||
if (!presets.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.IsNotificationDisabled = FormUtils.IsNotificationDisabled();
|
||||
|
||||
var (processId, isFullScreen) = FormUtils.GetForegroundProcessIdAndIfFullScreen();
|
||||
|
||||
if (processId == Environment.ProcessId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (processId > 0)
|
||||
{
|
||||
var process = context.RunningProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (process?.ProcessName?.Contains(".scr") ?? false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.ForegroundProcess = process;
|
||||
context.ForegroundProcessIsFullScreen = isFullScreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ForegroundProcess = null;
|
||||
context.ForegroundProcessIsFullScreen = false;
|
||||
}
|
||||
|
||||
var changedProcesses = new List<Process>();
|
||||
if (context.ForegroundProcess != null)
|
||||
{
|
||||
changedProcesses.Add(context.ForegroundProcess);
|
||||
}
|
||||
|
||||
if (context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen)
|
||||
{
|
||||
if (!context.LastFullScreenProcessName.Equals(context.ForegroundProcess.ProcessName))
|
||||
{
|
||||
context.LastFullScreenProcessName = context.ForegroundProcess.ProcessName;
|
||||
//Logger.Debug($"Foreground fullscreen app detected: {context.ForegroundProcess.ProcessName}");
|
||||
}
|
||||
|
||||
presets = presets.Where(p => p.Triggers.Any(t => t.Conditions.HasFlag(PresetConditionType.FullScreen)));
|
||||
context.WasFullScreen = true;
|
||||
}
|
||||
else if (context.WasFullScreen)
|
||||
{
|
||||
presets = presets.Where(p => p.Triggers.Any(t => !t.Conditions.HasFlag(PresetConditionType.FullScreen)));
|
||||
context.WasFullScreen = false;
|
||||
context.LastFullScreenProcessName = string.Empty;
|
||||
}
|
||||
|
||||
var isHDRActive = context.Devices.Any(d => d.IsUsingHDRPictureMode());
|
||||
|
||||
var isGsyncActive = LgDevice.ExternalServiceHandler("GsyncEnabled", new[] { "" });
|
||||
|
||||
var triggerContext = new PresetTriggerContext
|
||||
{
|
||||
IsHDRActive = isHDRActive,
|
||||
IsGsyncActive = isGsyncActive,
|
||||
ForegroundProcess = context.ForegroundProcess,
|
||||
ForegroundProcessIsFullScreen = context.ForegroundProcessIsFullScreen,
|
||||
ChangedProcesses = changedProcesses,
|
||||
IsNotificationDisabled = context.IsNotificationDisabled
|
||||
};
|
||||
|
||||
var toApplyPresets = new List<LgPreset>();
|
||||
foreach (var preset in presets)
|
||||
{
|
||||
if (preset.Triggers.Any(t => t.TriggerActive(triggerContext)))
|
||||
{
|
||||
toApplyPresets.Add(preset);
|
||||
}
|
||||
}
|
||||
|
||||
if (toApplyPresets.Any())
|
||||
{
|
||||
LgPreset toApplyPreset;
|
||||
if (toApplyPresets.Count > 1)
|
||||
{
|
||||
toApplyPreset = toApplyPresets.FirstOrDefault(p => p.Triggers.Any(t => !t.IncludedProcesses.Contains("*")));
|
||||
toApplyPreset = toApplyPreset != null ? toApplyPreset : toApplyPresets.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
toApplyPreset = toApplyPresets.First();
|
||||
}
|
||||
|
||||
if (_lastTriggeredPreset != toApplyPreset)
|
||||
{
|
||||
await ApplyPreset(toApplyPreset);
|
||||
|
||||
_lastTriggeredPreset = toApplyPreset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddCustomDevice(LgDevice device)
|
||||
{
|
||||
Devices.Add(device);
|
||||
}
|
||||
|
||||
internal void RemoveCustomDevice(LgDevice device)
|
||||
{
|
||||
Devices.Remove(device);
|
||||
|
||||
if (!string.IsNullOrEmpty(device.MacAddress))
|
||||
{
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
presets.ForEach(preset =>
|
||||
{
|
||||
preset.DeviceMacAddress = null;
|
||||
});
|
||||
}
|
||||
|
||||
if (SelectedDevice == device)
|
||||
{
|
||||
SelectedDevice = Devices.Any() ? Devices.First() : null;
|
||||
}
|
||||
}
|
||||
|
||||
public void PowerSettingChanged(WindowsPowerSetting setting)
|
||||
{
|
||||
|
||||
if (setting == WindowsPowerSetting.Off)
|
||||
{
|
||||
var devices = Devices.Where(d => d.PowerOffByWindows).ToList();
|
||||
PowerOffDevices(devices, PowerOnOffState.StandBy);
|
||||
}
|
||||
else if (setting == WindowsPowerSetting.On)
|
||||
{
|
||||
var devices = Devices.Where(d => d.PowerOnByWindows).ToList();
|
||||
PowerOnDevices(devices);
|
||||
}
|
||||
}
|
||||
|
||||
public void PowerOffDevices(IEnumerable<LgDevice> devices, PowerOnOffState state = PowerOnOffState.ShutDown)
|
||||
{
|
||||
if (!devices.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NativeMethods.GetAsyncKeyState(NativeConstants.VK_CONTROL) < 0 || NativeMethods.GetAsyncKeyState(NativeConstants.VK_RCONTROL) < 0)
|
||||
{
|
||||
Logger.Debug("Not powering off because CONTROL-key is down");
|
||||
return;
|
||||
}
|
||||
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
|
||||
try
|
||||
{
|
||||
if (state == PowerOnOffState.ShutDown)
|
||||
{
|
||||
var sleep = Config.ShutdownDelay;
|
||||
|
||||
Logger.Debug($"PowerOffDevices: Waiting for {sleep} milliseconds...");
|
||||
|
||||
while (sleep > 0 && _restartDetector?.PowerOffDetected == false)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (_restartDetector != null && (_restartDetector.RestartDetected || _restartDetector.IsRebootInProgress()))
|
||||
{
|
||||
Logger.Debug("Not powering off because of a restart");
|
||||
return;
|
||||
}
|
||||
|
||||
sleep -= 100;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Debug("Powering off tv(s)...");
|
||||
var tasks = new List<Task>();
|
||||
foreach (var device in devices)
|
||||
{
|
||||
var task = device.PowerOff(true);
|
||||
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
if (state == PowerOnOffState.StandBy)
|
||||
{
|
||||
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
|
||||
if (File.Exists(standByScript))
|
||||
{
|
||||
Utils.StartProcess(standByScript, hidden: true, wait: true);
|
||||
}
|
||||
}
|
||||
|
||||
var waitTask = Task.WhenAll(tasks.ToArray());
|
||||
Utils.WaitForTask(waitTask);
|
||||
Logger.Debug("Done powering off tv(s)");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
|
||||
}
|
||||
}
|
||||
|
||||
public void PowerOnDevices(IEnumerable<LgDevice> devices, PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
|
||||
{
|
||||
if (!devices.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkUserSession && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
Logger.Debug($"WakeAfterStartupOrResume: not waking because session info indicates no local session");
|
||||
return;
|
||||
}
|
||||
|
||||
var resumeScript = Path.Combine(Program.DataDir, "ResumeScript.bat");
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
if (!device.PowerOnAfterManualPowerOff && device.PoweredOffBy == LgDevice.PowerOffSource.Manually)
|
||||
{
|
||||
Logger.Debug($"[{device.Name}]: device was manually powered off by user, not powering on");
|
||||
continue;
|
||||
}
|
||||
|
||||
device.DisposeConnection();
|
||||
|
||||
var task = device.WakeAndConnectWithRetries(Config.PowerOnRetries);
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
if (File.Exists(resumeScript))
|
||||
{
|
||||
var waitTask = Task.WhenAll(tasks.ToArray());
|
||||
Utils.WaitForTask(waitTask);
|
||||
|
||||
Utils.StartProcess(resumeScript, hidden: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendConfigToService()
|
||||
{
|
||||
if (!Utils.IsServiceRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var json = JsonConvert.SerializeObject(Config);
|
||||
|
||||
var message = new SvcMessage
|
||||
{
|
||||
MessageType = SvcMessageType.SetLgConfig,
|
||||
Data = json
|
||||
};
|
||||
|
||||
PipeUtils.SendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
class LgServiceConfig
|
||||
{
|
||||
public bool PowerOnAfterStartup { get; set; }
|
||||
public int PowerOnDelayAfterResume { get; set; }
|
||||
public int PowerOnRetries { get; set; }
|
||||
public string PreferredMacAddress { get; set; }
|
||||
public string DeviceSearchKey { get; set; }
|
||||
public List<LgDevice> Devices { get; set; }
|
||||
public bool ShowRemoteControl { get; set; }
|
||||
public bool ShowAdvancedActions { get; set; }
|
||||
public string GameBarShortcut { get; set; }
|
||||
public int GameBarTop { get; set; }
|
||||
public int GameBarLeft { get; set; }
|
||||
public int ShutdownDelay { get; set; }
|
||||
public bool SetSelectedDeviceByPowerOn { get; set; }
|
||||
|
||||
public LgServiceConfig()
|
||||
{
|
||||
PowerOnDelayAfterResume = 5000;
|
||||
ShutdownDelay = 1000;
|
||||
DeviceSearchKey = "[LG]";
|
||||
PowerOnRetries = 10;
|
||||
Devices = new List<LgDevice>();
|
||||
GameBarTop = 100;
|
||||
GameBarLeft = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+55
-11
@@ -1,5 +1,5 @@
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlForm
|
||||
{
|
||||
@@ -29,21 +29,25 @@ namespace ColorControl
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RemoteControlForm));
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.lblUC = new System.Windows.Forms.Label();
|
||||
this.chkDirectMode = new System.Windows.Forms.CheckBox();
|
||||
this.pnlMouse = new System.Windows.Forms.Panel();
|
||||
this.lblPos = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// flowPanel
|
||||
//
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.Location = new System.Drawing.Point(12, 16);
|
||||
this.flowPanel.Location = new System.Drawing.Point(12, 185);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(787, 164);
|
||||
this.flowPanel.Size = new System.Drawing.Size(817, 120);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
|
||||
@@ -56,7 +60,7 @@ namespace ColorControl
|
||||
this.btnClose.FlatAppearance.BorderSize = 0;
|
||||
this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnClose.ForeColor = System.Drawing.Color.White;
|
||||
this.btnClose.Location = new System.Drawing.Point(796, 0);
|
||||
this.btnClose.Location = new System.Drawing.Point(826, 0);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Size = new System.Drawing.Size(14, 23);
|
||||
this.btnClose.TabIndex = 1;
|
||||
@@ -68,7 +72,7 @@ namespace ColorControl
|
||||
//
|
||||
this.lblUC.AutoSize = true;
|
||||
this.lblUC.ForeColor = System.Drawing.Color.Maroon;
|
||||
this.lblUC.Location = new System.Drawing.Point(130, 0);
|
||||
this.lblUC.Location = new System.Drawing.Point(12, 0);
|
||||
this.lblUC.Name = "lblUC";
|
||||
this.lblUC.Size = new System.Drawing.Size(100, 13);
|
||||
this.lblUC.TabIndex = 0;
|
||||
@@ -78,8 +82,8 @@ namespace ColorControl
|
||||
//
|
||||
this.chkDirectMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.chkDirectMode.AutoSize = true;
|
||||
this.chkDirectMode.ForeColor = System.Drawing.Color.DarkGray;
|
||||
this.chkDirectMode.Location = new System.Drawing.Point(12, 643);
|
||||
this.chkDirectMode.ForeColor = System.Drawing.Color.White;
|
||||
this.chkDirectMode.Location = new System.Drawing.Point(12, 718);
|
||||
this.chkDirectMode.Name = "chkDirectMode";
|
||||
this.chkDirectMode.Size = new System.Drawing.Size(105, 17);
|
||||
this.chkDirectMode.TabIndex = 2;
|
||||
@@ -93,9 +97,9 @@ namespace ColorControl
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.pnlMouse.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.pnlMouse.Location = new System.Drawing.Point(12, 191);
|
||||
this.pnlMouse.Location = new System.Drawing.Point(12, 311);
|
||||
this.pnlMouse.Name = "pnlMouse";
|
||||
this.pnlMouse.Size = new System.Drawing.Size(787, 446);
|
||||
this.pnlMouse.Size = new System.Drawing.Size(817, 401);
|
||||
this.pnlMouse.TabIndex = 3;
|
||||
this.pnlMouse.MouseEnter += new System.EventHandler(this.pnlMouse_MouseEnter);
|
||||
this.pnlMouse.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pnlMouse_MouseMove);
|
||||
@@ -106,17 +110,53 @@ namespace ColorControl
|
||||
this.lblPos.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.lblPos.AutoSize = true;
|
||||
this.lblPos.ForeColor = System.Drawing.Color.Maroon;
|
||||
this.lblPos.Location = new System.Drawing.Point(719, 643);
|
||||
this.lblPos.Location = new System.Drawing.Point(749, 718);
|
||||
this.lblPos.Name = "lblPos";
|
||||
this.lblPos.Size = new System.Drawing.Size(0, 13);
|
||||
this.lblPos.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.ForeColor = System.Drawing.Color.White;
|
||||
this.label1.Location = new System.Drawing.Point(12, 26);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(538, 52);
|
||||
this.label1.TabIndex = 4;
|
||||
this.label1.Text = resources.GetString("label1.Text");
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.ForeColor = System.Drawing.Color.White;
|
||||
this.label2.Location = new System.Drawing.Point(231, 91);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(216, 52);
|
||||
this.label2.TabIndex = 5;
|
||||
this.label2.Text = "Mouse:\r\n- left button: activate/open item\r\n- right button: go back\r\n- wheel: scro" +
|
||||
"ll through menus (only vertically)";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.ForeColor = System.Drawing.Color.White;
|
||||
this.label3.Location = new System.Drawing.Point(12, 91);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(180, 91);
|
||||
this.label3.TabIndex = 6;
|
||||
this.label3.Text = "Keyboard keys:\r\n- arrow keys: navigate the menu\r\n- backspace: go back\r\n- escape: " +
|
||||
"exit\r\n- S: open picture settings\r\n- H: open Home\r\n- media keys: volume up/down/m" +
|
||||
"ute";
|
||||
//
|
||||
// RemoteControlForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Black;
|
||||
this.ClientSize = new System.Drawing.Size(811, 660);
|
||||
this.ClientSize = new System.Drawing.Size(841, 735);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.lblPos);
|
||||
this.Controls.Add(this.pnlMouse);
|
||||
this.Controls.Add(this.chkDirectMode);
|
||||
@@ -132,6 +172,7 @@ namespace ColorControl
|
||||
this.Text = "RemoteControlForm";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.RemoteControlForm_FormClosed);
|
||||
this.Load += new System.EventHandler(this.RemoteControlForm_Load);
|
||||
this.Shown += new System.EventHandler(this.RemoteControlForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyDown);
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp);
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
@@ -149,5 +190,8 @@ namespace ColorControl
|
||||
private System.Windows.Forms.CheckBox chkDirectMode;
|
||||
private System.Windows.Forms.Panel pnlMouse;
|
||||
private System.Windows.Forms.Label lblPos;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlForm : Form
|
||||
{
|
||||
@@ -116,24 +116,24 @@ namespace ColorControl
|
||||
{
|
||||
if (chkDirectMode.Checked)
|
||||
{
|
||||
MessageForms.InfoOk(
|
||||
@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
|
||||
Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
|
||||
// MessageForms.InfoOk(
|
||||
//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
|
||||
//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
|
||||
|
||||
Keyboard keys:
|
||||
- arrow keys: navigate the menu
|
||||
- backspace: go back
|
||||
- escape: exit
|
||||
- S: open picture settings
|
||||
- media keys: volume up/down/mute
|
||||
//Keyboard keys:
|
||||
//- arrow keys: navigate the menu
|
||||
//- backspace: go back
|
||||
//- escape: exit
|
||||
//- S: open picture settings
|
||||
//- media keys: volume up/down/mute
|
||||
|
||||
Mouse:
|
||||
- left button: activate/open item
|
||||
- right button: go back
|
||||
- wheel: scroll through menus (only vertically)
|
||||
//Mouse:
|
||||
//- left button: activate/open item
|
||||
//- right button: go back
|
||||
//- wheel: scroll through menus (only vertically)
|
||||
|
||||
Press F1 to stop Direct Mode."
|
||||
);
|
||||
//Press F1 to stop Direct Mode."
|
||||
// );
|
||||
|
||||
if (_mouseService == null)
|
||||
{
|
||||
@@ -216,6 +216,11 @@ Press F1 to stop Direct Mode."
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
chkDirectMode.Checked = true;
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
HandleMouse(e);
|
||||
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="label1.Text" xml:space="preserve">
|
||||
<value>This form allows you to control the TV directly via your keyboard and mouse.
|
||||
Note that your mouse will be captured in a box and that due to pointer acceleration the control will not be perfect.
|
||||
|
||||
Press F1 to stop Direct Mode.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,81 @@
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlPanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.pbRemote = new System.Windows.Forms.PictureBox();
|
||||
this.btnRcDirect = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// pbRemote
|
||||
//
|
||||
this.pbRemote.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pbRemote.Image = global::ColorControl.Properties.Resources.LG_remote;
|
||||
this.pbRemote.Location = new System.Drawing.Point(0, 0);
|
||||
this.pbRemote.Name = "pbRemote";
|
||||
this.pbRemote.Size = new System.Drawing.Size(129, 468);
|
||||
this.pbRemote.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pbRemote.TabIndex = 0;
|
||||
this.pbRemote.TabStop = false;
|
||||
this.pbRemote.Click += new System.EventHandler(this.pbRemote_Click);
|
||||
this.pbRemote.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbRemote_MouseMove);
|
||||
//
|
||||
// btnRcDirect
|
||||
//
|
||||
this.btnRcDirect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnRcDirect.Location = new System.Drawing.Point(71, 7);
|
||||
this.btnRcDirect.Name = "btnRcDirect";
|
||||
this.btnRcDirect.Size = new System.Drawing.Size(55, 23);
|
||||
this.btnRcDirect.TabIndex = 3;
|
||||
this.btnRcDirect.Text = "Direct";
|
||||
this.btnRcDirect.UseVisualStyleBackColor = true;
|
||||
this.btnRcDirect.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// RemoteControlPanel
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.Controls.Add(this.btnRcDirect);
|
||||
this.Controls.Add(this.pbRemote);
|
||||
this.Name = "RemoteControlPanel";
|
||||
this.Size = new System.Drawing.Size(129, 468);
|
||||
this.Load += new System.EventHandler(this.RemoteControlPanel_Load);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox pbRemote;
|
||||
private System.Windows.Forms.Button btnRcDirect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
using ColorControl.Common;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlPanel : UserControl
|
||||
{
|
||||
private static int Range = 35;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private LgService _lgService;
|
||||
private List<LgPreset> _buttons;
|
||||
private RemoteControlForm _remoteControlForm;
|
||||
private Bitmap _backupImage;
|
||||
private LgPreset _lastHoveredPreset;
|
||||
|
||||
public RemoteControlPanel(LgService lgService, List<LgPreset> buttons)
|
||||
{
|
||||
_lgService = lgService;
|
||||
_buttons = buttons;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
_backupImage = new Bitmap(pbRemote.Image);
|
||||
|
||||
var txtCoords = Utils.GetResourceFile("RC_coords.txt");
|
||||
|
||||
foreach (var line in txtCoords.Split('\n'))
|
||||
{
|
||||
var columns = line.Split(';');
|
||||
|
||||
var name = columns[0];
|
||||
var preset = _buttons.FirstOrDefault(p => p.name.Equals(name));
|
||||
if (preset != null)
|
||||
{
|
||||
preset.X = int.Parse(columns[1]);
|
||||
preset.Y = int.Parse(columns[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteControlPanel_Load(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
if (e is MouseEventArgs)
|
||||
{
|
||||
var preset = (LgPreset)(sender as Button).Tag;
|
||||
|
||||
var _ = _lgService.ApplyPreset(preset);
|
||||
}
|
||||
}
|
||||
|
||||
private void pbRemote_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (e is MouseEventArgs mouseArgs)
|
||||
{
|
||||
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
|
||||
|
||||
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
|
||||
|
||||
if (preset != null)
|
||||
{
|
||||
if (preset.steps.Contains("POWER") && _lgService.SelectedDevice?.CurrentState != LgDevice.PowerState.Active)
|
||||
{
|
||||
Logger.Debug("Executing WOL instead of POWER");
|
||||
|
||||
var wolPreset = _buttons.FirstOrDefault(p => p.name.Equals("Wol"));
|
||||
if (wolPreset != null)
|
||||
{
|
||||
preset = wolPreset;
|
||||
}
|
||||
}
|
||||
|
||||
var _ = _lgService.ApplyPreset(preset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (int, int) GetCoordsInImage(int curX, int curY)
|
||||
{
|
||||
var width = pbRemote.Width;
|
||||
var aspect = 0.27D;
|
||||
var currentAspect = (double)pbRemote.Width / pbRemote.Height;
|
||||
if (currentAspect > aspect)
|
||||
{
|
||||
width = (int)(aspect * pbRemote.Height);
|
||||
curX = curX - ((pbRemote.Width - width) / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var height = (int)(pbRemote.Width / aspect);
|
||||
curY = curY - ((pbRemote.Height - height) / 2);
|
||||
}
|
||||
|
||||
var factor = 405D / width;
|
||||
|
||||
var posX = (int)(curX * factor);
|
||||
var posY = (int)(curY * factor);
|
||||
|
||||
return (posX, posY);
|
||||
}
|
||||
|
||||
private void btnRcDirect_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs)
|
||||
{
|
||||
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
|
||||
|
||||
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
|
||||
|
||||
var refresh = false;
|
||||
|
||||
if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset))
|
||||
{
|
||||
var image = (Bitmap)pbRemote.Image;
|
||||
|
||||
for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++)
|
||||
{
|
||||
for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++)
|
||||
{
|
||||
image.SetPixel(x, y, _backupImage.GetPixel(x, y));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
if (preset != null && preset != _lastHoveredPreset)
|
||||
{
|
||||
var image = (Bitmap)pbRemote.Image;
|
||||
var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2);
|
||||
|
||||
using (var g = Graphics.FromImage(image))
|
||||
{
|
||||
using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White)))
|
||||
{
|
||||
g.FillEllipse(cloud_brush, rec);
|
||||
}
|
||||
}
|
||||
|
||||
//for (var y = preset.Y - 30; y < preset.Y + 30; y++)
|
||||
//{
|
||||
// for (var x = preset.X - 30; x < preset.X + 30; x++)
|
||||
// {
|
||||
// var pixel = image.GetPixel(x, y);
|
||||
|
||||
// image.SetPixel(x, y, Color.FromArgb(150, 0, 0));
|
||||
|
||||
// }
|
||||
//}
|
||||
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
if (refresh)
|
||||
{
|
||||
pbRemote.Refresh();
|
||||
}
|
||||
|
||||
_lastHoveredPreset = preset;
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
|
||||
{
|
||||
var buttons = _lgService.GetRemoteControlButtons();
|
||||
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_remoteControlForm.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,40 @@
|
||||
using Newtonsoft.Json;
|
||||
using NvAPIWrapper.Display;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl.Services.NVIDIA
|
||||
{
|
||||
public class ColorDataConverter : JsonConverter<ColorData>
|
||||
{
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ColorData ReadJson(JsonReader reader, Type objectType, ColorData existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
reader.Read();
|
||||
while (reader.TokenType == JsonToken.PropertyName)
|
||||
{
|
||||
var propertyName = reader.Value;
|
||||
|
||||
reader.Read();
|
||||
var value = reader.Value;
|
||||
|
||||
result.Add(propertyName.ToString(), value);
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
return NvPreset.GenerateColorData(result);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, ColorData value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using NvAPIWrapper.Display;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.NVIDIA
|
||||
{
|
||||
class NvDisplayInfo
|
||||
{
|
||||
@@ -1,13 +1,19 @@
|
||||
using NvAPIWrapper.Display;
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.Common;
|
||||
using nspector.Common;
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.NVIDIA
|
||||
{
|
||||
class NvPreset : PresetBase
|
||||
{
|
||||
public static NvService NvService { get; set; }
|
||||
|
||||
public bool applyColorData { get; set; }
|
||||
public ColorData colorData { get; set; }
|
||||
public bool applyHDR { get; set; }
|
||||
@@ -17,12 +23,17 @@ namespace ColorControl
|
||||
public bool ditheringEnabled { get; set; }
|
||||
public bool applyRefreshRate { get; set; }
|
||||
public uint refreshRate { get; set; }
|
||||
public bool applyResolution { get; set; }
|
||||
public uint resolutionWidth { get; set; }
|
||||
public uint resolutionHeight { get; set; }
|
||||
public bool primaryDisplay { get; set; }
|
||||
public string displayName { get; set; }
|
||||
public uint ditheringBits { get; set; }
|
||||
public uint ditheringMode { get; set; }
|
||||
public bool applyDriverSettings { get; set; }
|
||||
public Dictionary<uint, uint> driverSettings { get; set; }
|
||||
|
||||
public NvPreset()
|
||||
public NvPreset() : base()
|
||||
{
|
||||
applyColorData = true;
|
||||
applyDithering = false;
|
||||
@@ -32,9 +43,14 @@ namespace ColorControl
|
||||
toggleHDR = false;
|
||||
applyRefreshRate = false;
|
||||
refreshRate = 60;
|
||||
applyResolution = false;
|
||||
resolutionWidth = 0;
|
||||
resolutionHeight = 0;
|
||||
primaryDisplay = true;
|
||||
ditheringBits = 1;
|
||||
ditheringMode = 4;
|
||||
applyDriverSettings = false;
|
||||
driverSettings = new Dictionary<uint, uint>();
|
||||
}
|
||||
|
||||
public NvPreset(ColorData colorData) : this()
|
||||
@@ -43,7 +59,7 @@ namespace ColorControl
|
||||
this.colorData = colorData;
|
||||
}
|
||||
|
||||
public NvPreset(NvPreset preset): this()
|
||||
public NvPreset(NvPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
@@ -51,7 +67,7 @@ namespace ColorControl
|
||||
displayName = preset.displayName;
|
||||
|
||||
var colorData = preset.colorData;
|
||||
this.colorData = new ColorData(colorData.ColorFormat, dynamicRange: colorData.DynamicRange, colorDepth: colorData.ColorDepth, colorSelectionPolicy: colorData.SelectionPolicy);
|
||||
this.colorData = new ColorData(colorData.ColorFormat, dynamicRange: colorData.DynamicRange, colorDepth: colorData.ColorDepth, colorSelectionPolicy: ColorDataSelectionPolicy.User);
|
||||
applyColorData = preset.applyColorData;
|
||||
|
||||
applyHDR = preset.applyHDR;
|
||||
@@ -61,6 +77,11 @@ namespace ColorControl
|
||||
ditheringEnabled = preset.ditheringEnabled;
|
||||
applyRefreshRate = preset.applyRefreshRate;
|
||||
refreshRate = preset.refreshRate;
|
||||
applyResolution = preset.applyResolution;
|
||||
resolutionWidth = preset.resolutionWidth;
|
||||
resolutionHeight = preset.resolutionHeight;
|
||||
applyDriverSettings = preset.applyDriverSettings;
|
||||
driverSettings = new Dictionary<uint, uint>(preset.driverSettings);
|
||||
}
|
||||
|
||||
public NvPreset Clone()
|
||||
@@ -72,14 +93,15 @@ namespace ColorControl
|
||||
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
//return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" };
|
||||
return new[] { "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut", "Apply on startup" };
|
||||
return new[] { "Name", "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Resolution|120", "Dithering", "HDR", "Driver settings|300", "Shortcut", "Apply on startup" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var display = string.Format("{0}", primaryDisplay ? "Primary" : displayName);
|
||||
values.Add(display);
|
||||
|
||||
@@ -88,15 +110,22 @@ namespace ColorControl
|
||||
values.Add(colorSettings);
|
||||
values.Add(string.Format("{0}: {1}Hz", applyRefreshRate ? "Included" : "Excluded", refreshRate));
|
||||
|
||||
if (applyResolution || resolutionWidth > 0)
|
||||
{
|
||||
values.Add(string.Format("{0}: {1}x{2}", applyResolution ? "Included" : "Excluded", resolutionWidth, resolutionHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
values.Add(string.Format("Excluded"));
|
||||
}
|
||||
|
||||
var dithering = GetDitheringDescription();
|
||||
|
||||
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", dithering));
|
||||
values.Add(string.Format("{0}: {1}", applyHDR ? "Included" : "Excluded", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled"));
|
||||
|
||||
//values.Add(colorData.ColorDepth.ToString());
|
||||
//values.Add(colorData.ColorFormat.ToString());
|
||||
//values.Add(colorData.DynamicRange.ToString());
|
||||
//values.Add(toggleHDR.ToString());
|
||||
values.Add(string.Format("{0}: {1}", applyDriverSettings ? "Included" : "Excluded", GetDriverSettingsDescription()));
|
||||
|
||||
values.Add(shortcut);
|
||||
|
||||
values.Add(string.Format("{0}", config?.NvPresetId_ApplyOnStartup == id ? "Yes" : string.Empty));
|
||||
@@ -104,6 +133,27 @@ namespace ColorControl
|
||||
return values;
|
||||
}
|
||||
|
||||
public string GetDriverSettingsDescription()
|
||||
{
|
||||
if (driverSettings.Count == 0)
|
||||
{
|
||||
return "None";
|
||||
}
|
||||
|
||||
var values = new List<string>();
|
||||
|
||||
foreach (var driverSetting in driverSettings)
|
||||
{
|
||||
var settingMeta = NvService.GetSettingMeta(driverSetting.Key);
|
||||
|
||||
var settingValue = settingMeta.DwordValues.FirstOrDefault(s => s.Value == driverSetting.Value);
|
||||
|
||||
values.Add($"{settingMeta.SettingName}: {settingValue?.ValueName ?? "Unknown"}");
|
||||
}
|
||||
|
||||
return string.Join(", ", values);
|
||||
}
|
||||
|
||||
public string GetDitheringDescription(string disabledText = "Disabled")
|
||||
{
|
||||
var dithering = ditheringEnabled ? string.Empty : disabledText;
|
||||
@@ -139,34 +189,52 @@ namespace ColorControl
|
||||
return presets;
|
||||
}
|
||||
|
||||
public string GetTextForMenuItem()
|
||||
public override string GetTextForMenuItem()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (displayName != null)
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
sb.AppendFormat("Display: {0} / ", displayName);
|
||||
}
|
||||
if (applyColorData)
|
||||
{
|
||||
var colorSettings = string.Format("Format: {0}, {1}, {2}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange);
|
||||
sb.Append(colorSettings);
|
||||
sb.Append(name);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyRefreshRate)
|
||||
else
|
||||
{
|
||||
sb.AppendFormat("{0}Hz", refreshRate);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDithering)
|
||||
{
|
||||
var dithering = GetDitheringDescription("No");
|
||||
sb.AppendFormat("Dithering: {0}", dithering);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyHDR)
|
||||
{
|
||||
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
|
||||
sb.Append(" / ");
|
||||
if (displayName != null)
|
||||
{
|
||||
sb.AppendFormat("Display: {0} / ", displayName);
|
||||
}
|
||||
if (applyColorData)
|
||||
{
|
||||
var colorSettings = string.Format("Format: {0}, {1}, {2}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange);
|
||||
sb.Append(colorSettings);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyRefreshRate)
|
||||
{
|
||||
sb.AppendFormat("{0}Hz", refreshRate);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyResolution)
|
||||
{
|
||||
sb.AppendFormat("{0}x{1}", resolutionWidth, resolutionHeight);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDithering)
|
||||
{
|
||||
var dithering = GetDitheringDescription("No");
|
||||
sb.AppendFormat("Dithering: {0}", dithering);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyHDR)
|
||||
{
|
||||
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDriverSettings)
|
||||
{
|
||||
sb.AppendFormat("DRS: {0}", GetDriverSettingsDescription());
|
||||
sb.Append(" / ");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString(0, Math.Max(0, sb.Length - 3));
|
||||
@@ -196,13 +264,27 @@ namespace ColorControl
|
||||
{
|
||||
dynamicRange = (ColorDataDynamicRange)Enum.ToObject(typeof(ColorDataDynamicRange), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out value))
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out _))
|
||||
{
|
||||
selectionPolicy = (ColorDataSelectionPolicy)Enum.ToObject(typeof(ColorDataSelectionPolicy), value);
|
||||
selectionPolicy =
|
||||
colorDepth == ColorDataDepth.Default &&
|
||||
format >= ColorDataFormat.Default &&
|
||||
colorimetry >= ColorDataColorimetry.Default &&
|
||||
dynamicRange == ColorDataDynamicRange.Auto ?
|
||||
ColorDataSelectionPolicy.BestQuality : selectionPolicy;
|
||||
}
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy);
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy, desktopColorDepth: ColorDataDesktopDepth.Default);
|
||||
}
|
||||
|
||||
internal void UpdateDriverSetting(SettingItem settingItem, uint settingValue)
|
||||
{
|
||||
driverSettings[settingItem.SettingId] = settingValue;
|
||||
}
|
||||
|
||||
internal void ResetDriverSetting(uint settingId)
|
||||
{
|
||||
driverSettings.Remove(settingId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,753 @@
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.Common;
|
||||
using nspector.Common;
|
||||
using nspector.Common.Meta;
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
using NvAPIWrapper.Native.GPU.Structures;
|
||||
using NWin32.NativeTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl.Services.NVIDIA
|
||||
{
|
||||
enum NvDitherState
|
||||
{
|
||||
[Description("Auto")]
|
||||
Auto = 0,
|
||||
[Description("Enabled")]
|
||||
Enabled = 1,
|
||||
[Description("Disabled")]
|
||||
Disabled = 2
|
||||
}
|
||||
|
||||
enum NvDitherBits
|
||||
{
|
||||
[Description("6-bit")]
|
||||
Bits6 = 0,
|
||||
[Description("8-bit")]
|
||||
Bits8 = 1,
|
||||
[Description("10-bit")]
|
||||
Bits10 = 2
|
||||
}
|
||||
|
||||
enum NvDitherMode
|
||||
{
|
||||
[Description("Spatial Dynamic")]
|
||||
SpatialDynamic = 0,
|
||||
[Description("Spatial Static")]
|
||||
SpatialStatic = 1,
|
||||
[Description("Spatial Dynamic 2x2")]
|
||||
SpatialDynamic2x2 = 2,
|
||||
[Description("Spatial Static 2x2")]
|
||||
SpatialStatic2x2 = 3,
|
||||
[Description("Temporal")]
|
||||
Temporal = 4
|
||||
}
|
||||
|
||||
class NvService : GraphicsService<NvPreset>
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
[DllImport(@"nvapi64", EntryPoint = @"nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl,
|
||||
PreserveSig = true)]
|
||||
private static extern IntPtr NvAPI64_QueryInterface(uint interfaceId);
|
||||
|
||||
public delegate int NvAPI_Disp_SetDitherControl(
|
||||
[In] PhysicalGPUHandle physicalGpu,
|
||||
[In] uint OutputId,
|
||||
[In] uint state,
|
||||
[In] uint bits,
|
||||
[In] uint mode
|
||||
);
|
||||
|
||||
public delegate int NvAPI_Disp_GetDitherControl(
|
||||
[In] uint DisplayId,
|
||||
[MarshalAs(UnmanagedType.Struct)] ref NV_GPU_DITHER_CONTROL_V1 ditherControl);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NV_GPU_DITHER_CONTROL_V1
|
||||
{
|
||||
public uint version;
|
||||
public int state;
|
||||
public int bits;
|
||||
public int mode;
|
||||
public uint bitsCaps;
|
||||
public uint modeCaps;
|
||||
};
|
||||
|
||||
public override string ServiceName => "NVIDIA";
|
||||
|
||||
private Display _currentDisplay;
|
||||
private string _baseProfileName = "";
|
||||
|
||||
private DrsSettingsService _drs;
|
||||
private DrsSettingsMetaService _meta;
|
||||
private List<SettingItem> _settings = new List<SettingItem>();
|
||||
|
||||
private Semaphore _semaphore = new Semaphore(1, 1);
|
||||
|
||||
public const uint DRS_GSYNC_APPLICATION_MODE = 294973784;
|
||||
public const uint DRS_VSYNC_CONTROL = 11041231;
|
||||
//public const uint DRS_ULTRA_LOW_LATENCY = 0x10835000;
|
||||
public const uint DRS_PRERENDERED_FRAMES = 8102046;
|
||||
public const uint DRS_FRAME_RATE_LIMITER_V3 = 277041154;
|
||||
|
||||
private static readonly List<uint> _driverSettingIds = new() { DRS_GSYNC_APPLICATION_MODE, DRS_VSYNC_CONTROL, DRS_PRERENDERED_FRAMES, DRS_FRAME_RATE_LIMITER_V3 };
|
||||
|
||||
public NvService(string dataPath) : base(dataPath, "NvPresets.json")
|
||||
{
|
||||
NvPreset.NvService = this;
|
||||
|
||||
AddJsonConverter(new ColorDataConverter());
|
||||
|
||||
LoadPresets();
|
||||
}
|
||||
|
||||
public static bool ExecutePresetAsync(string idOrName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var nvService = new NvService(Program.DataDir);
|
||||
|
||||
var result = nvService.ApplyPreset(idOrName);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Console.WriteLine("Preset not found or error while executing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error executing preset: " + ex.ToLogString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Display GetCurrentDisplay()
|
||||
{
|
||||
if (!HasDisplaysAttached())
|
||||
{
|
||||
_currentDisplay = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_currentDisplay == null)
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays()[0];
|
||||
}
|
||||
|
||||
return _currentDisplay;
|
||||
}
|
||||
|
||||
public override bool HasDisplaysAttached(bool reinitialize = false)
|
||||
{
|
||||
if (reinitialize)
|
||||
{
|
||||
try
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Could not reinitialize NVIDIA API: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return DisplayDevice.GetGDIPrimaryDisplayDevice() != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return reinitialize ? false : HasDisplaysAttached(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
protected override List<NvPreset> GetDefaultPresets()
|
||||
{
|
||||
return NvPreset.GetDefaultPresets();
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
Utils.WriteObject(_presetsFilename, _presets);
|
||||
}
|
||||
|
||||
private void SetCurrentDisplay(NvPreset preset)
|
||||
{
|
||||
if (preset.primaryDisplay)
|
||||
{
|
||||
var displayId = DisplayDevice.GetGDIPrimaryDisplayDevice().DisplayId;
|
||||
_currentDisplay = Display.GetDisplays().FirstOrDefault(x => x.DisplayDevice.DisplayId == displayId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays().FirstOrDefault(x => x.Name.Equals(preset.displayName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool ApplyPreset(string idOrName)
|
||||
{
|
||||
var preset = GetPresetByIdOrName(idOrName);
|
||||
if (preset != null)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ApplyPreset(NvPreset preset)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
}
|
||||
|
||||
public bool ApplyPreset(NvPreset preset, AppContext appContext)
|
||||
{
|
||||
var result = true;
|
||||
|
||||
if (!HasDisplaysAttached())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetCurrentDisplay(preset);
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hdrEnabled = IsHDREnabled();
|
||||
|
||||
var newHdrEnabled = preset.applyHDR && (preset.HDREnabled || (preset.toggleHDR && !hdrEnabled));
|
||||
var applyHdr = preset.applyHDR && (preset.toggleHDR || preset.HDREnabled != hdrEnabled);
|
||||
|
||||
if (preset.applyColorData && (ColorDataDiffers(preset.colorData) || (!newHdrEnabled && preset.applyColorData && preset.colorData.Colorimetry != ColorDataColorimetry.Auto)))
|
||||
{
|
||||
//if (hdrEnabled)
|
||||
//{
|
||||
// SetHDRState(display, false);
|
||||
|
||||
// applyHdr = false;
|
||||
//}
|
||||
|
||||
if (preset.applyRefreshRate || preset.applyResolution)
|
||||
{
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
if ((preset.applyRefreshRate && preset.refreshRate < timing.Extra.RefreshRate) || (preset.applyResolution && preset.resolutionWidth < timing.HorizontalVisible))
|
||||
{
|
||||
SetMode(preset.applyResolution ? preset.resolutionWidth : 0, preset.applyResolution ? preset.resolutionHeight : 0, preset.applyRefreshRate ? preset.refreshRate : 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
//if (preset.applyRefreshRate)
|
||||
//{
|
||||
// var timing = display.DisplayDevice.CurrentTiming;
|
||||
// if (preset.refreshRate < timing.Extra.RefreshRate)
|
||||
// {
|
||||
// SetRefreshRate(preset.refreshRate, true);
|
||||
// }
|
||||
//}
|
||||
|
||||
try
|
||||
{
|
||||
display.DisplayDevice.SetColorData(preset.colorData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"SetColorData threw an exception: {ex.Message}");
|
||||
result = false;
|
||||
}
|
||||
|
||||
//if (hdrEnabled && newHdrEnabled)
|
||||
//{
|
||||
// SetHDRState(display, true, useSwitch: appContext.StartUpParams.NoGui);
|
||||
|
||||
// applyHdr = false;
|
||||
//}
|
||||
}
|
||||
|
||||
if (applyHdr)
|
||||
{
|
||||
var colorData = preset.applyColorData ? preset.colorData : display.DisplayDevice.CurrentColorData;
|
||||
|
||||
SetHDRState(display, newHdrEnabled, colorData, appContext.StartUpParams.NoGui);
|
||||
}
|
||||
|
||||
if (preset.applyRefreshRate || preset.applyResolution)
|
||||
{
|
||||
if (!SetMode(preset.applyResolution ? preset.resolutionWidth : 0, preset.applyResolution ? preset.resolutionHeight : 0, preset.applyRefreshRate ? preset.refreshRate : 0, true))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
//var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
//if (preset.refreshRate != timing.Extra.RefreshRate && !SetRefreshRate(preset.refreshRate, true))
|
||||
//{
|
||||
// result = false;
|
||||
//}
|
||||
}
|
||||
|
||||
if (preset.applyDithering)
|
||||
{
|
||||
if (!SetDithering(preset.ditheringEnabled ? NvDitherState.Enabled : NvDitherState.Disabled, preset: preset))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.applyDriverSettings)
|
||||
{
|
||||
SetDriverSettings(preset);
|
||||
}
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SetDriverSettings(NvPreset preset)
|
||||
{
|
||||
var settings = new List<KeyValuePair<uint, string>>();
|
||||
|
||||
foreach (var keyValue in preset.driverSettings)
|
||||
{
|
||||
var settingMeta = _meta.GetSettingMeta(keyValue.Key);
|
||||
|
||||
var value = settingMeta.DwordValues.FirstOrDefault(v => v.Value == keyValue.Value);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
settings.Add(new KeyValuePair<uint, string>(keyValue.Key, value.ValueName));
|
||||
}
|
||||
|
||||
_drs.StoreSettingsToProfile(_baseProfileName, settings);
|
||||
}
|
||||
|
||||
private ColorData GetCurrentColorData(Display display)
|
||||
{
|
||||
try
|
||||
{
|
||||
return display.DisplayDevice.CurrentColorData;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Error while reading current color data: " + e.Message);
|
||||
return new ColorData();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ColorDataDiffers(ColorData colorData)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
|
||||
var currentColorData = GetCurrentColorData(display);
|
||||
|
||||
var settingRange = colorData.DynamicRange == ColorDataDynamicRange.Auto ? colorData.ColorFormat == ColorDataFormat.RGB ? ColorDataDynamicRange.VESA : ColorDataDynamicRange.CEA : colorData.DynamicRange;
|
||||
|
||||
var settingSpace = colorData.Colorimetry;
|
||||
|
||||
return colorData.ColorFormat != currentColorData.ColorFormat || colorData.ColorDepth != currentColorData.ColorDepth || settingRange != currentColorData.DynamicRange || settingSpace != currentColorData.Colorimetry;
|
||||
}
|
||||
|
||||
private void SetHDRState(Display display, bool enabled, ColorData colorData = null, bool useSwitch = false)
|
||||
{
|
||||
if (useSwitch)
|
||||
{
|
||||
if (enabled != IsHDREnabled())
|
||||
{
|
||||
CCD.SetHDRState(enabled, display.Name);
|
||||
//ToggleHDR();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CCD.SetHDRState(enabled, display.Name);
|
||||
|
||||
//var newMaster = new MasteringDisplayColorData();
|
||||
//var hdr = new HDRColorData(enabled ? ColorDataHDRMode.UHDA : ColorDataHDRMode.Off, newMaster, colorData?.ColorFormat, colorData?.DynamicRange, colorData?.ColorDepth);
|
||||
|
||||
//display.DisplayDevice.SetHDRColorData(hdr);
|
||||
|
||||
// HDR will not always be disabled this way, then we can only disable it through the display settings
|
||||
//if (!enabled && IsHDREnabled())
|
||||
//{
|
||||
// CCD.SetHDRState(enabled);
|
||||
// //ToggleHDR();
|
||||
//}
|
||||
//else if (enabled)
|
||||
//{
|
||||
// // Currectly there seems to be a bug that after enabling HDR via NVAPI, some settings are only applied upon opening the Display Settings...
|
||||
// ////OpenDisplaySettings();
|
||||
//}
|
||||
}
|
||||
|
||||
public bool SetDithering(NvDitherState state, uint bits = 1, uint mode = 4, NvPreset preset = null)
|
||||
{
|
||||
var result = true;
|
||||
|
||||
var ptr = NvAPI64_QueryInterface(0xDF0DFCDD);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_SetDitherControl)) as NvAPI_Disp_SetDitherControl;
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
|
||||
var gpuHandle = displayDevice.PhysicalGPU.Handle;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
|
||||
if (state == NvDitherState.Auto)
|
||||
{
|
||||
// These seem to be ignored in Auto-state
|
||||
bits = 0;
|
||||
mode = 0;
|
||||
}
|
||||
else if (preset != null)
|
||||
{
|
||||
bits = preset.ditheringBits;
|
||||
mode = preset.ditheringMode;
|
||||
}
|
||||
|
||||
var resultValue = delegateValue(gpuHandle, displayId, (uint)state, bits, mode);
|
||||
if (resultValue != 0)
|
||||
{
|
||||
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"Could not set dithering because the function NvAPI_Disp_SetDitherControl could not be found");
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public NV_GPU_DITHER_CONTROL_V1 GetDithering()
|
||||
{
|
||||
var dither = new NV_GPU_DITHER_CONTROL_V1 { version = 0x10018 };
|
||||
|
||||
var ptr = NvAPI64_QueryInterface(0x932AC8FB);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl;
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
dither.state = -1;
|
||||
return dither;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
|
||||
var result = delegateValue(displayId, ref dither);
|
||||
if (result != 0)
|
||||
{
|
||||
Logger.Error($"Could not get dithering because NvAPI_Disp_GetDitherControl returned a non-zero return code: {result}");
|
||||
dither.state = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dither.state = -2;
|
||||
}
|
||||
|
||||
return dither;
|
||||
}
|
||||
|
||||
public bool SetMode(uint resolutionWidth = 0, uint resolutionHeight = 0, uint refreshRate = 0, bool updateRegistry = false)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var desktopRect = display.DisplayDevice.ScanOutInformation.SourceDesktopRectangle;
|
||||
|
||||
if (refreshRate == 0)
|
||||
{
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
refreshRate = (uint)timing.Extra.RefreshRate;
|
||||
}
|
||||
|
||||
if (resolutionWidth == 0)
|
||||
{
|
||||
resolutionWidth = (uint)desktopRect.Width;
|
||||
resolutionHeight = (uint)desktopRect.Height;
|
||||
}
|
||||
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
|
||||
return SetRefreshRateInternal(display.Name, (int)refreshRate, portrait, (int)resolutionWidth, (int)resolutionHeight, updateRegistry);
|
||||
}
|
||||
|
||||
public List<uint> GetAvailableRefreshRates(NvPreset preset = null)
|
||||
{
|
||||
if (preset != null)
|
||||
{
|
||||
SetCurrentDisplay(preset);
|
||||
}
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return new List<uint>();
|
||||
}
|
||||
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
var desktopRect = display.DisplayDevice.ScanOutInformation.SourceDesktopRectangle;
|
||||
|
||||
return GetAvailableRefreshRatesInternal(display.Name, portrait, desktopRect.Width, desktopRect.Height);
|
||||
}
|
||||
|
||||
public List<DEVMODEA> GetAvailableResolutions(NvPreset preset = null)
|
||||
{
|
||||
if (preset != null)
|
||||
{
|
||||
SetCurrentDisplay(preset);
|
||||
}
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return new List<DEVMODEA>();
|
||||
}
|
||||
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
return GetAvailableResolutionsInternal(display.Name, portrait, (uint)(timing.Extra.RefreshRate));
|
||||
}
|
||||
|
||||
public bool IsHDREnabled()
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
var hdr = displayDevice.HDRColorData;
|
||||
return hdr?.HDRMode == ColorDataHDRMode.UHDA;
|
||||
}
|
||||
|
||||
public Display[] GetDisplays()
|
||||
{
|
||||
return Display.GetDisplays();
|
||||
}
|
||||
|
||||
public List<NvDisplayInfo> GetDisplayInfos()
|
||||
{
|
||||
try
|
||||
{
|
||||
var displays = GetDisplays();
|
||||
var list = new List<NvDisplayInfo>();
|
||||
|
||||
RefreshProfileSettings();
|
||||
|
||||
foreach (var display in displays)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add("Current settings");
|
||||
|
||||
var name = display.Name;
|
||||
|
||||
var screen = Screen.AllScreens.FirstOrDefault(x => x.DeviceName.Equals(name));
|
||||
if (screen != null)
|
||||
{
|
||||
name += " (" + screen.DeviceFriendlyName() + ")";
|
||||
}
|
||||
|
||||
values.Add(name);
|
||||
|
||||
var colorData = GetCurrentColorData(display);
|
||||
|
||||
var colorSettings = string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry);
|
||||
|
||||
values.Add(colorSettings);
|
||||
|
||||
var refreshRate = display.DisplayDevice.CurrentTiming.Extra.RefreshRate;
|
||||
|
||||
values.Add($"{refreshRate}Hz");
|
||||
|
||||
var desktopRect = display.DisplayDevice.ScanOutInformation.SourceDesktopRectangle;
|
||||
|
||||
values.Add($"{desktopRect.Width}x{desktopRect.Height}");
|
||||
|
||||
var ditherInfo = GetDithering();
|
||||
|
||||
string dithering;
|
||||
if (ditherInfo.state == -1)
|
||||
{
|
||||
dithering = "Error";
|
||||
}
|
||||
else
|
||||
{
|
||||
var state = (NvDitherState)ditherInfo.state;
|
||||
dithering = state switch { NvDitherState.Disabled => "Disabled", NvDitherState.Auto => "Auto: ", _ => string.Empty };
|
||||
if (state is NvDitherState.Enabled or NvDitherState.Auto)
|
||||
{
|
||||
var ditherBitsDescription = ((NvDitherBits)ditherInfo.bits).GetDescription();
|
||||
var ditherModeDescription = ((NvDitherMode)ditherInfo.mode).GetDescription();
|
||||
dithering = string.Format("{0}{1} {2}", dithering, ditherBitsDescription, ditherModeDescription);
|
||||
}
|
||||
}
|
||||
|
||||
values.Add(dithering);
|
||||
|
||||
var hdrEnabled = IsHDREnabled();
|
||||
values.Add(hdrEnabled ? "Yes" : "No");
|
||||
|
||||
var settings = GetVisibleSettings();
|
||||
var drsValues = new List<string>();
|
||||
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var settingMeta = GetSettingMeta(setting.SettingId);
|
||||
|
||||
var settingValue = settingMeta.DwordValues.FirstOrDefault(s => s.ValueName == setting.ValueText);
|
||||
|
||||
drsValues.Add($"{settingMeta.SettingName}: {settingValue?.ValueName ?? "Unknown"}");
|
||||
}
|
||||
|
||||
values.Add(string.Join(", ", drsValues));
|
||||
|
||||
var infoLine = string.Format("{0}: {1}, {2}Hz, HDR: {3}", name, colorSettings, refreshRate, hdrEnabled ? "Yes" : "No");
|
||||
|
||||
var displayInfo = new NvDisplayInfo(display, values, infoLine);
|
||||
|
||||
list.Add(displayInfo);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Error while getting displays: " + e.ToLogString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<SettingItem> GetVisibleSettings()
|
||||
{
|
||||
return GetSettings().Where(s => _driverSettingIds.Contains(s.SettingId)).ToList();
|
||||
}
|
||||
|
||||
public SettingMeta GetSettingMeta(uint settingId)
|
||||
{
|
||||
return _meta.GetSettingMeta(settingId);
|
||||
}
|
||||
|
||||
public bool IsGsyncEnabled()
|
||||
{
|
||||
var setting = GetSettings().FirstOrDefault(s => s.SettingId == DRS_GSYNC_APPLICATION_MODE);
|
||||
if (setting == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var settingMeta = GetSettingMeta(setting.SettingId);
|
||||
var settingValue = settingMeta.DwordValues.FirstOrDefault(s => s.ValueName == setting.ValueText);
|
||||
|
||||
return settingValue.Value >= 1;
|
||||
}
|
||||
|
||||
protected List<SettingItem> GetSettings()
|
||||
{
|
||||
_semaphore.WaitOne();
|
||||
try
|
||||
{
|
||||
if (!_settings.Any())
|
||||
{
|
||||
RefreshProfileSettings();
|
||||
}
|
||||
|
||||
return _settings;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
NvAPIWrapper.NVIDIA.Initialize();
|
||||
_initialized = true;
|
||||
|
||||
_drs = DrsServiceLocator.SettingService;
|
||||
_meta = DrsServiceLocator.MetaService;
|
||||
|
||||
_drs.GetProfileNames(ref _baseProfileName, false);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
RefreshProfileSettings();
|
||||
});
|
||||
}
|
||||
|
||||
private void RefreshProfileSettings()
|
||||
{
|
||||
_semaphore.WaitOne();
|
||||
try
|
||||
{
|
||||
var applications = new Dictionary<string, string>();
|
||||
var normalSettings = _drs.GetSettingsForProfile(_baseProfileName, SettingViewMode.Normal, ref applications);
|
||||
|
||||
_settings = new List<SettingItem>();
|
||||
_settings.AddRange(normalSettings);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Uninitialize()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
NvAPIWrapper.NVIDIA.Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using ColorControl.Common;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
@@ -7,16 +9,119 @@ namespace ColorControl
|
||||
public const string RunningFromScheduledTaskParam = "--scheduled";
|
||||
public const string ActivateChromeFontFixParam = "--activate-chrome-font-fix";
|
||||
public const string DeactivateChromeFontFixParam = "--deactivate-chrome-font-fix";
|
||||
public const string ExecuteHelpParam = "--help";
|
||||
public const string ExecuteNvidiaPresetParam = "--nvpreset";
|
||||
public const string ExecuteAmdPresetParam = "--amdpreset";
|
||||
public const string ExecuteLgPresetParam = "--lgpreset";
|
||||
public const string NoGuiParam = "--nogui";
|
||||
public const string EnableAutoStartParam = "--enable-auto-start";
|
||||
public const string DisableAutoStartParam = "--disable-auto-start";
|
||||
public const string SetProcessAffinityParam = "--set-process-affinity";
|
||||
public const string SetProcessPriorityParam = "--set-process-priority";
|
||||
public const string StartElevatedParam = "--elevated";
|
||||
public const string SendWakeOnLanParam = "--send-wol";
|
||||
public const string InstallServiceParam = "--install-service";
|
||||
public const string UninstallServiceParam = "--uninstall-service";
|
||||
public const string StartServiceParam = "--start-service";
|
||||
public const string StopServiceParam = "--stop-service";
|
||||
|
||||
public bool RunningFromScheduledTask { get; private set; }
|
||||
public bool ActivateChromeFontFix { get; private set; }
|
||||
public bool DeactivateChromeFontFix { get; private set; }
|
||||
public bool ExecuteHelp { get; private set; }
|
||||
public bool ExecuteNvidiaPreset { get; private set; }
|
||||
public string NvidiaPresetIdOrName { get; private set; }
|
||||
public bool ExecuteAmdPreset { get; private set; }
|
||||
public string AmdPresetIdOrName { get; private set; }
|
||||
public bool ExecuteLgPreset { get; private set; }
|
||||
public string LgPresetName { get; private set; }
|
||||
public bool NoGui { get; set; }
|
||||
public bool EnableAutoStart { get; private set; }
|
||||
public TaskRunLevel AutoStartRunLevel { get; private set; } = TaskRunLevel.LUA;
|
||||
public bool DisableAutoStart { get; private set; }
|
||||
public bool SetProcessAffinity { get; private set; }
|
||||
public int ProcessId { get; private set; }
|
||||
public uint AffinityMask { get; private set; }
|
||||
public bool SetProcessPriority { get; private set; }
|
||||
public uint PriorityClass { get; private set; }
|
||||
public bool StartElevated { get; private set; }
|
||||
public bool SendWol { get; private set; }
|
||||
public string WolMacAddress { get; private set; }
|
||||
public string WolIpAddress { get; private set; }
|
||||
public bool InstallService { get; private set; }
|
||||
public bool UninstallService { get; private set; }
|
||||
public bool StartService { get; private set; }
|
||||
public bool StopService { get; private set; }
|
||||
|
||||
public static StartUpParams Parse(IEnumerable<string> args)
|
||||
{
|
||||
var settings = new StartUpParams();
|
||||
var parseNameParam = string.Empty;
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parseNameParam))
|
||||
{
|
||||
switch (parseNameParam)
|
||||
{
|
||||
case ExecuteNvidiaPresetParam:
|
||||
{
|
||||
settings.NvidiaPresetIdOrName = arg;
|
||||
break;
|
||||
}
|
||||
case ExecuteAmdPresetParam:
|
||||
{
|
||||
settings.AmdPresetIdOrName = arg;
|
||||
break;
|
||||
}
|
||||
case ExecuteLgPresetParam:
|
||||
{
|
||||
settings.LgPresetName = arg;
|
||||
break;
|
||||
}
|
||||
case SetProcessAffinityParam:
|
||||
{
|
||||
if (settings.ProcessId == 0)
|
||||
{
|
||||
settings.ProcessId = int.Parse(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
settings.AffinityMask = uint.Parse(arg);
|
||||
break;
|
||||
}
|
||||
case SetProcessPriorityParam:
|
||||
{
|
||||
if (settings.ProcessId == 0)
|
||||
{
|
||||
settings.ProcessId = int.Parse(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
settings.PriorityClass = uint.Parse(arg);
|
||||
break;
|
||||
}
|
||||
case SendWakeOnLanParam:
|
||||
{
|
||||
if (settings.WolMacAddress == null)
|
||||
{
|
||||
settings.WolMacAddress = arg;
|
||||
continue;
|
||||
}
|
||||
|
||||
settings.WolIpAddress = arg;
|
||||
break;
|
||||
}
|
||||
case EnableAutoStartParam:
|
||||
{
|
||||
settings.AutoStartRunLevel = (TaskRunLevel)Utils.ParseInt(arg, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parseNameParam = string.Empty;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (arg.ToLowerInvariant())
|
||||
{
|
||||
case RunningFromScheduledTaskParam:
|
||||
@@ -34,6 +139,89 @@ namespace ColorControl
|
||||
settings.DeactivateChromeFontFix = true;
|
||||
break;
|
||||
}
|
||||
case ExecuteHelpParam:
|
||||
{
|
||||
settings.ExecuteHelp = true;
|
||||
settings.NoGui = true;
|
||||
break;
|
||||
}
|
||||
case ExecuteNvidiaPresetParam:
|
||||
{
|
||||
settings.ExecuteNvidiaPreset = true;
|
||||
parseNameParam = ExecuteNvidiaPresetParam;
|
||||
break;
|
||||
}
|
||||
case ExecuteAmdPresetParam:
|
||||
{
|
||||
settings.ExecuteAmdPreset = true;
|
||||
parseNameParam = ExecuteAmdPresetParam;
|
||||
break;
|
||||
}
|
||||
case ExecuteLgPresetParam:
|
||||
{
|
||||
settings.ExecuteLgPreset = true;
|
||||
parseNameParam = ExecuteLgPresetParam;
|
||||
break;
|
||||
}
|
||||
case NoGuiParam:
|
||||
{
|
||||
settings.NoGui = true;
|
||||
break;
|
||||
}
|
||||
case EnableAutoStartParam:
|
||||
{
|
||||
settings.EnableAutoStart = true;
|
||||
parseNameParam = EnableAutoStartParam;
|
||||
break;
|
||||
}
|
||||
case DisableAutoStartParam:
|
||||
{
|
||||
settings.DisableAutoStart = true;
|
||||
break;
|
||||
}
|
||||
case SetProcessAffinityParam:
|
||||
{
|
||||
settings.SetProcessAffinity = true;
|
||||
parseNameParam = SetProcessAffinityParam;
|
||||
break;
|
||||
}
|
||||
case SetProcessPriorityParam:
|
||||
{
|
||||
settings.SetProcessPriority = true;
|
||||
parseNameParam = SetProcessPriorityParam;
|
||||
break;
|
||||
}
|
||||
case StartElevatedParam:
|
||||
{
|
||||
settings.StartElevated = true;
|
||||
break;
|
||||
}
|
||||
case SendWakeOnLanParam:
|
||||
{
|
||||
settings.SendWol = true;
|
||||
parseNameParam = SendWakeOnLanParam;
|
||||
break;
|
||||
}
|
||||
case InstallServiceParam:
|
||||
{
|
||||
settings.InstallService = true;
|
||||
break;
|
||||
}
|
||||
case UninstallServiceParam:
|
||||
{
|
||||
settings.UninstallService = true;
|
||||
break;
|
||||
}
|
||||
case StartServiceParam:
|
||||
{
|
||||
settings.StartService = true;
|
||||
break;
|
||||
}
|
||||
case StopServiceParam:
|
||||
{
|
||||
settings.StopService = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Services.LG;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ColorControl.Svc
|
||||
{
|
||||
public sealed class ColorControlBackgroundService : BackgroundService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private static string LgConfigFile = "LgConfig.json";
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
LgConfigFile = Path.Combine(Program.DataDir, LgConfigFile);
|
||||
|
||||
//var lgService = new LgService(Program.DataDir, true);
|
||||
//lgService.InstallEventHandlers(true);
|
||||
|
||||
await WakeDevicesAsync();
|
||||
|
||||
var securityIdentifier = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
var ps = new PipeSecurity();
|
||||
ps.AddAccessRule(new PipeAccessRule(securityIdentifier, PipeAccessRights.ReadWrite, AccessControlType.Allow));
|
||||
|
||||
Logger.Debug("EXECUTING BACKGROUND");
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
Logger.Debug("WAITING FOR MESSAGE...");
|
||||
|
||||
var pipeServer = NamedPipeServerStreamAcl.Create("testpipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, ps);
|
||||
|
||||
await pipeServer.WaitForConnectionAsync(stoppingToken);
|
||||
|
||||
var ss = new StreamString(pipeServer);
|
||||
|
||||
var message = ss.ReadString();
|
||||
|
||||
//Logger.Debug("MESSAGE RECEIVED: " + message);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await HandleMessageAsync(message);
|
||||
|
||||
var resultJson = JsonConvert.SerializeObject(result);
|
||||
|
||||
//Logger.Debug("SENDING RESULT: " + resultJson);
|
||||
|
||||
ss.WriteString(resultJson);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, $"Error while handling message: {message}");
|
||||
|
||||
var result = SvcResultMessage.FromResult(false, ex.Message);
|
||||
var resultJson = JsonConvert.SerializeObject(result);
|
||||
ss.WriteString(resultJson);
|
||||
}
|
||||
|
||||
pipeServer.Close();
|
||||
//pipeServer.Disconnect();
|
||||
}
|
||||
Logger.Debug("STOPPING...");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Logger.Debug("Service stopping");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "{Message}", ex.Message);
|
||||
|
||||
// Terminates this process and returns an exit code to the operating system.
|
||||
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
|
||||
// performs one of two scenarios:
|
||||
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
|
||||
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
|
||||
//
|
||||
// In order for the Windows Service Management system to leverage configured
|
||||
// recovery options, we need to terminate the process with a non-zero exit code.
|
||||
//Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<SvcResultMessage> HandleMessageAsync(string json)
|
||||
{
|
||||
var message = JsonConvert.DeserializeObject<SvcMessage>(json);
|
||||
|
||||
var result = message.MessageType switch
|
||||
{
|
||||
SvcMessageType.SetLgConfig => HandleSetLgConfigMessage(message),
|
||||
SvcMessageType.GetLog => HandleGetLogMessage(message),
|
||||
SvcMessageType.ClearLog => HandleClearLogMessage(message),
|
||||
SvcMessageType.ExecuteRpc => HandleExecuteRpcCommand(message),
|
||||
SvcMessageType.ExecuteUpdate => await HandleExecuteUpdateCommandAsync(JsonConvert.DeserializeObject<SvcInstallUpdateMessage>(json)),
|
||||
SvcMessageType.RestartAfterUpdate => HandleRestartAfterUpdateMessage(),
|
||||
_ => SvcResultMessage.FromResult(false, "Unexpected message type")
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SvcResultMessage HandleExecuteRpcCommand(SvcMessage message)
|
||||
{
|
||||
var args = message.Data.Split(' ');
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
return SvcResultMessage.FromResult(Program.HandleStartupParams(startUpParams, null));
|
||||
}
|
||||
|
||||
private SvcResultMessage HandleSetLgConfigMessage(SvcMessage message)
|
||||
{
|
||||
var data = message.Data;
|
||||
|
||||
Utils.WriteText(LgConfigFile, data);
|
||||
|
||||
return SvcResultMessage.FromResult(true);
|
||||
}
|
||||
|
||||
private SvcResultMessage HandleGetLogMessage(SvcMessage _)
|
||||
{
|
||||
var logFile = Program.LogFilename;
|
||||
|
||||
var data = Utils.ReadText(logFile);
|
||||
|
||||
return SvcResultMessage.FromResult(data);
|
||||
}
|
||||
|
||||
private SvcResultMessage HandleClearLogMessage(SvcMessage _)
|
||||
{
|
||||
var logFile = Program.LogFilename;
|
||||
|
||||
if (File.Exists(logFile))
|
||||
{
|
||||
File.Delete(logFile);
|
||||
}
|
||||
|
||||
return SvcResultMessage.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task<SvcResultMessage> HandleExecuteUpdateCommandAsync(SvcInstallUpdateMessage message)
|
||||
{
|
||||
var localFile = Path.Combine(Program.DataDir, "update.zip");
|
||||
|
||||
await Utils.DownloadFileAsync(message.DownloadUrl, localFile);
|
||||
|
||||
var updatePath = Path.Combine(Program.DataDir, "ColorControl");
|
||||
|
||||
var directory = new DirectoryInfo(updatePath);
|
||||
if (directory.Exists)
|
||||
{
|
||||
directory.Delete(true);
|
||||
}
|
||||
|
||||
Utils.UnZipFile(localFile, Program.DataDir);
|
||||
|
||||
Utils.UpdateFiles(message.ClientPath, updatePath);
|
||||
|
||||
return SvcResultMessage.FromResult(true);
|
||||
}
|
||||
|
||||
private SvcResultMessage HandleRestartAfterUpdateMessage()
|
||||
{
|
||||
Utils.StartProcess("cmd.exe", $@"/C net stop ""{Utils.SERVICE_NAME}"" && net start ""{Utils.SERVICE_NAME}""", true);
|
||||
|
||||
return SvcResultMessage.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task WakeDevicesAsync()
|
||||
{
|
||||
if (!UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(LgConfigFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var config = Utils.DeserializeJson<LgServiceConfig>(LgConfigFile);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var wakeDevices = config.Devices.Where(d => d.PowerOnAfterStartup).ToList();
|
||||
|
||||
foreach (var wakeDevice in wakeDevices)
|
||||
{
|
||||
for (var i = 0; i < config.PowerOnRetries; i++)
|
||||
{
|
||||
WOL.WakeFunction(wakeDevice.MacAddress, wakeDevice.IpAddress);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
var result = Utils.PingHost(wakeDevice.IpAddress);
|
||||
|
||||
if (result)
|
||||
{
|
||||
Logger.Debug($"Device {wakeDevice.Name} is pingable, stopping WOL");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace ColorControl.Svc
|
||||
{
|
||||
public enum SvcMessageType
|
||||
{
|
||||
SetLgConfig = 1,
|
||||
GetLog = 2,
|
||||
ClearLog = 3,
|
||||
ExecuteRpc = 10,
|
||||
ExecuteUpdate = 20,
|
||||
RestartAfterUpdate = 21,
|
||||
}
|
||||
|
||||
public class SvcMessage
|
||||
{
|
||||
public SvcMessageType MessageType { get; set; }
|
||||
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
public class SvcInstallUpdateMessage : SvcMessage
|
||||
{
|
||||
public SvcInstallUpdateMessage()
|
||||
{
|
||||
MessageType = SvcMessageType.ExecuteUpdate;
|
||||
}
|
||||
|
||||
public string DownloadUrl { get; set; }
|
||||
public string ClientPath { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace ColorControl.Svc
|
||||
{
|
||||
public class SvcResultMessage
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
|
||||
public string Data { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
|
||||
public static SvcResultMessage FromResult(bool result, string errorMessage = null) => new SvcResultMessage { Result = result, ErrorMessage = errorMessage };
|
||||
public static SvcResultMessage FromResult(string data) => new SvcResultMessage { Result = true, Data = data };
|
||||
}
|
||||
}
|
||||
@@ -1,655 +0,0 @@
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using NStandard;
|
||||
using NWin32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Deployment.Application;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Windows.Devices.Enumeration;
|
||||
using Windows.Devices.Enumeration.Pnp;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
[Flags]
|
||||
public enum ModKeys : int
|
||||
{
|
||||
Alt = 1,
|
||||
Control = 2,
|
||||
Shift = 4,
|
||||
Win = 8
|
||||
}
|
||||
|
||||
public const int WM_BRINGTOFRONT = NativeConstants.WM_USER + 1;
|
||||
|
||||
public static string PKEY_PNPX_IpAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12297";
|
||||
public static string PKEY_PNPX_PhysicalAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12294";
|
||||
|
||||
public delegate void PCREATE_PROCESS_NOTIFY_ROUTINE(IntPtr ParentId, IntPtr ProcessId, bool Create);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd);
|
||||
|
||||
//[DllImport("Wtsapi32.dll")]
|
||||
//public extern static bool WTSRegisterSessionNotification(IntPtr hWnd, uint dwFlags);
|
||||
|
||||
//[DllImport("Wtsapi32.dll")]
|
||||
//public extern static bool WTSUnRegisterSessionNotification(IntPtr hWnd);
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
private static bool WinKeyDown = false;
|
||||
|
||||
public static Bitmap SubPixelShift(Bitmap bitmap)
|
||||
{
|
||||
Bitmap bitmap2 = (Bitmap)bitmap.Clone();
|
||||
|
||||
int shift = -1;
|
||||
bool shiftRed = true;
|
||||
bool shiftGreen = true;
|
||||
bool shiftBlue = false;
|
||||
|
||||
for (int i = 0; i < bitmap.Width; i++)
|
||||
{
|
||||
for (int j = 0; j < bitmap.Height; j++)
|
||||
{
|
||||
Color pixel = bitmap.GetPixel(i, j);
|
||||
|
||||
if (shift > 0 && i < bitmap.Width - shift)
|
||||
{
|
||||
Color pixel2 = bitmap2.GetPixel(i + shift, j);
|
||||
|
||||
Color pixel3 = Color.FromArgb(shiftRed ? pixel.R : pixel2.R, shiftGreen ? pixel.G : pixel2.G, shiftBlue ? pixel.B : pixel2.B);
|
||||
|
||||
bitmap2.SetPixel(i + shift, j, pixel3);
|
||||
|
||||
}
|
||||
else if (i + shift >= 0)
|
||||
{
|
||||
Color pixel2 = bitmap2.GetPixel(i + shift, j);
|
||||
|
||||
Color pixel3 = Color.FromArgb(shiftRed ? pixel.R : pixel2.R, shiftGreen ? pixel.G : pixel2.G, shiftBlue ? pixel.B : pixel2.B);
|
||||
|
||||
bitmap2.SetPixel(i + shift, j, pixel3);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap2;
|
||||
//bitmap2.Save("d:\\ss_shifted.bmp");
|
||||
}
|
||||
|
||||
public static string GetDataPath(string subDir = "")
|
||||
{
|
||||
var path = Directory.GetParent(Application.UserAppDataPath).FullName;
|
||||
if (!string.IsNullOrEmpty(subDir))
|
||||
{
|
||||
path = Path.Combine(path, subDir);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static (int mods, int key) ParseShortcut(string shortcut)
|
||||
{
|
||||
int mods = 0;
|
||||
var key = 0;
|
||||
|
||||
shortcut = shortcut.Replace(",", "");
|
||||
shortcut = shortcut.Replace("+", "");
|
||||
shortcut = shortcut.Replace(" ", " ");
|
||||
var parts = shortcut.Split(' ');
|
||||
|
||||
if (parts.Length > 0)
|
||||
{
|
||||
for (int i = 0; i + 1 < parts.Length; i++)
|
||||
{
|
||||
switch (parts[i])
|
||||
{
|
||||
case "Alt":
|
||||
mods += (int)ModKeys.Alt;
|
||||
break;
|
||||
case "Control":
|
||||
mods += (int)ModKeys.Control;
|
||||
break;
|
||||
case "Shift":
|
||||
mods += (int)ModKeys.Shift;
|
||||
break;
|
||||
case "Win":
|
||||
mods += (int)ModKeys.Win;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var keyName = parts[parts.Length - 1];
|
||||
|
||||
key = (int)Enum.Parse(typeof(Keys), keyName);
|
||||
}
|
||||
|
||||
return (mods, key);
|
||||
}
|
||||
|
||||
internal static bool ExecuteElevated(string args)
|
||||
{
|
||||
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location, args)
|
||||
{
|
||||
Verb = "runas", // indicates to elevate privileges
|
||||
};
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
EnableRaisingEvents = true, // enable WaitForExit()
|
||||
StartInfo = info
|
||||
};
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
process.WaitForExit(); // sleep calling process thread until evoked process exit
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("ExecuteElevated: " + e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsChromeFixInstalled()
|
||||
{
|
||||
var key = Registry.ClassesRoot.OpenSubKey(@"ChromeHTML\shell\open\command");
|
||||
return key != null && key.GetValue(null).ToString().Contains("--disable-lcd-text");
|
||||
}
|
||||
|
||||
internal static bool InstallChromeFix(bool install)
|
||||
{
|
||||
var argument = "--disable-lcd-text";
|
||||
|
||||
var key = Registry.ClassesRoot.OpenSubKey(@"ChromeHTML\shell\open\command", true);
|
||||
if (key != null)
|
||||
{
|
||||
var value = key.GetValue(null).ToString();
|
||||
value = value.Replace("chrome.exe\" " + argument + " --", "chrome.exe\" --");
|
||||
if (install)
|
||||
{
|
||||
value = value.Replace("chrome.exe\" --", "chrome.exe\" " + argument + " --");
|
||||
}
|
||||
key.SetValue(null, value);
|
||||
}
|
||||
|
||||
var roamingFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
||||
UpdateShortcut(Path.Combine(roamingFolder, @"Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Google Chrome.lnk"), argument, !install);
|
||||
UpdateShortcut(Path.Combine(roamingFolder, @"Microsoft\Internet Explorer\Quick Launch\Google Chrome.lnk"), argument, !install);
|
||||
|
||||
var allUsersStartMenuFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu);
|
||||
UpdateShortcut(Path.Combine(allUsersStartMenuFolder, @"Programs\Google Chrome.lnk"), argument, !install);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsChromeInstalled()
|
||||
{
|
||||
var key = Registry.ClassesRoot.OpenSubKey(@"ChromeHTML\shell\open\command");
|
||||
return key != null;
|
||||
}
|
||||
|
||||
internal static void GetRegistryKeyValue(string keyname, string valueName, bool deepSearch = false)
|
||||
{
|
||||
var key = Registry.LocalMachine.OpenSubKey(keyname);
|
||||
|
||||
foreach (var subKeyName in key.GetSubKeyNames())
|
||||
{
|
||||
var subKey = key.OpenSubKey(subKeyName);
|
||||
if (subKey.GetValueNames().Contains(valueName))
|
||||
{
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static bool UpdateShortcut(string path, string arguments, bool removeArguments = false)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell();
|
||||
|
||||
IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(path);
|
||||
|
||||
shortcut.Arguments = shortcut.Arguments.Replace(arguments, "");
|
||||
if (!removeArguments)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(shortcut.Arguments))
|
||||
{
|
||||
shortcut.Arguments = shortcut.Arguments + " ";
|
||||
}
|
||||
shortcut.Arguments = shortcut.Arguments + arguments;
|
||||
}
|
||||
|
||||
// save it / create
|
||||
shortcut.Save();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string GetDeviceProperty(PnpObject device, string propertyName)
|
||||
{
|
||||
object objValue;
|
||||
string value = null;
|
||||
|
||||
//foreach (var key in device.Properties.Keys)
|
||||
//{
|
||||
// Debug.WriteLine(key);
|
||||
//}
|
||||
|
||||
if (device.Properties.TryGetValue(propertyName, out objValue))
|
||||
{
|
||||
if (objValue is string[])
|
||||
{
|
||||
var arr = objValue as string[];
|
||||
if (arr.Length > 0)
|
||||
{
|
||||
value = arr[0];
|
||||
}
|
||||
}
|
||||
else if (objValue is byte[])
|
||||
{
|
||||
value = BitConverter.ToString((byte[])objValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = objValue?.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static async Task<List<PnpDev>> GetPnpDevices(string deviceName)
|
||||
{
|
||||
var devices = new List<PnpDev>();
|
||||
|
||||
var list = await DeviceInformation.FindAllAsync("", new List<string>());
|
||||
|
||||
var reqs = new List<string>
|
||||
{
|
||||
PKEY_PNPX_IpAddress,
|
||||
PKEY_PNPX_PhysicalAddress
|
||||
};
|
||||
|
||||
foreach (var dev in list)
|
||||
{
|
||||
string name = dev.Name;
|
||||
if (/*dev.IsEnabled &&*/ dev.Name.Contains(deviceName))
|
||||
{
|
||||
var guid = dev.Properties["System.Devices.DeviceInstanceId"];
|
||||
|
||||
var aqs = "System.Devices.DeviceInstanceId:=\"" + guid + "\"";
|
||||
var list2 = await PnpObject.FindAllAsync(PnpObjectType.Device, reqs, aqs);
|
||||
|
||||
foreach (var dev2 in list2)
|
||||
{
|
||||
var ipAddress = GetDeviceProperty(dev2, "System.Devices.IpAddress");
|
||||
var macAddress = GetDeviceProperty(dev2, PKEY_PNPX_PhysicalAddress);
|
||||
|
||||
if (!devices.Any(x => x.Name.Equals(name) && x.IpAddress != null && x.IpAddress.Equals(ipAddress)))
|
||||
{
|
||||
var device = new PnpDev(dev, dev2, name, ipAddress, macAddress);
|
||||
devices.Add(device);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
public static void WaitForTask(System.Threading.Tasks.Task task)
|
||||
{
|
||||
while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
Application.DoEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetNotifyIconText(NotifyIcon ni, string text)
|
||||
{
|
||||
text = text.Substring(0, Math.Min(text.Length, 127));
|
||||
Type t = typeof(NotifyIcon);
|
||||
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
t.GetField("text", hidden).SetValue(ni, text);
|
||||
if ((bool)t.GetField("added", hidden).GetValue(ni))
|
||||
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
|
||||
}
|
||||
|
||||
internal static Image GenerateGradientBitmap(int width, int height)
|
||||
{
|
||||
var bitmap = new Bitmap(512, height);
|
||||
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var pixel = Color.FromArgb(i, i, i);
|
||||
|
||||
for (var h = 0; h < height; h++)
|
||||
{
|
||||
bitmap.SetPixel(i * 2, h, pixel);
|
||||
bitmap.SetPixel(i * 2 + 1, h, pixel);
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static string GetDescription<T>(this T e) where T : IConvertible
|
||||
{
|
||||
if (e is Enum)
|
||||
{
|
||||
var type = e.GetType();
|
||||
var values = Enum.GetValues(type);
|
||||
|
||||
foreach (int val in values)
|
||||
{
|
||||
if (val == e.ToInt32(CultureInfo.InvariantCulture))
|
||||
{
|
||||
var memInfo = type.GetMember(type.GetEnumName(val));
|
||||
var descriptionAttribute = memInfo[0]
|
||||
.GetCustomAttributes(typeof(DescriptionAttribute), false)
|
||||
.FirstOrDefault() as DescriptionAttribute;
|
||||
|
||||
if (descriptionAttribute != null)
|
||||
{
|
||||
return descriptionAttribute.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; // could also return string.Empty
|
||||
}
|
||||
|
||||
public static List<string> GetDescriptions<T>() where T : IConvertible
|
||||
{
|
||||
var list = new List<string>();
|
||||
foreach (var enumValue in Enum.GetValues(typeof(T)))
|
||||
{
|
||||
list.Add(((T)enumValue).GetDescription());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void RegisterTask(string taskName, bool enabled)
|
||||
{
|
||||
var file = Assembly.GetExecutingAssembly().Location;
|
||||
var directory = Path.GetDirectoryName(file);
|
||||
|
||||
try
|
||||
{
|
||||
using (TaskService ts = new TaskService())
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
TaskDefinition td = ts.NewTask();
|
||||
td.RegistrationInfo.Description = "Start ColorControl";
|
||||
|
||||
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name });
|
||||
|
||||
td.Actions.Add(new ExecAction(file, StartUpParams.RunningFromScheduledTaskParam, directory));
|
||||
|
||||
ts.RootFolder.RegisterTaskDefinition(taskName, td);
|
||||
}
|
||||
else
|
||||
{
|
||||
ts.RootFolder.DeleteTask(taskName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageForms.ErrorOk("Could not create/delete task: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TaskExists(string taskName, bool update)
|
||||
{
|
||||
var file = Assembly.GetExecutingAssembly().Location;
|
||||
using (TaskService ts = new TaskService())
|
||||
{
|
||||
var task = ts.RootFolder.Tasks.FirstOrDefault(x => x.Name.Equals(taskName));
|
||||
|
||||
if (task != null)
|
||||
{
|
||||
if (update && ApplicationDeployment.IsNetworkDeployed)
|
||||
{
|
||||
var action = task.Definition.Actions.FirstOrDefault(x => x.ActionType == TaskActionType.Execute);
|
||||
if (action != null)
|
||||
{
|
||||
var execAction = action as ExecAction;
|
||||
if (!execAction.Path.Equals(file))
|
||||
{
|
||||
RegisterTask(taskName, false);
|
||||
RegisterTask(taskName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterShortcut(IntPtr handle, int id, string shortcut, bool clear = false)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
UnregisterHotKey(handle, id);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(shortcut))
|
||||
{
|
||||
var (mods, key) = ParseShortcut(shortcut);
|
||||
RegisterHotKey(handle, id, mods, key);
|
||||
}
|
||||
}
|
||||
|
||||
public static void BuildDropDownMenu(ToolStripMenuItem mnuParent, string name, Type enumType, object colorData, string propertyName, EventHandler clickEvent)
|
||||
{
|
||||
PropertyInfo property;
|
||||
var subMenuItems = mnuParent.DropDownItems.Find("miColorSettings_" + name, false);
|
||||
ToolStripMenuItem subMenuItem;
|
||||
if (subMenuItems.Length == 0)
|
||||
{
|
||||
subMenuItem = (ToolStripMenuItem)mnuParent.DropDownItems.Add(name);
|
||||
subMenuItem.Name = "miColorSettings_" + name;
|
||||
|
||||
property = colorData.GetType().GetDeclaredProperty(propertyName);
|
||||
subMenuItem.Tag = property;
|
||||
|
||||
foreach (var enumValue in Enum.GetValues(enumType))
|
||||
{
|
||||
var item = subMenuItem.DropDownItems.Add(enumValue.ToString());
|
||||
item.Tag = enumValue;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subMenuItem = (ToolStripMenuItem)subMenuItems[0];
|
||||
property = (PropertyInfo)subMenuItem.Tag;
|
||||
}
|
||||
|
||||
var value = property.GetValue(colorData);
|
||||
|
||||
foreach (var item in subMenuItem.DropDownItems)
|
||||
{
|
||||
if (item is ToolStripMenuItem)
|
||||
{
|
||||
var menuItem = (ToolStripMenuItem)item;
|
||||
if (menuItem.Tag != null)
|
||||
{
|
||||
menuItem.Checked = menuItem.Tag.Equals(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatKeyboardShortcut(KeyEventArgs keyEvent)
|
||||
{
|
||||
var pressedModifiers = keyEvent.Modifiers;
|
||||
|
||||
//Debug.WriteLine("KD: " + e.Modifiers + ", " + e.KeyCode);
|
||||
|
||||
var shortcutString = (pressedModifiers > 0 ? pressedModifiers.ToString() : "");
|
||||
if (keyEvent.KeyCode == Keys.LWin || WinKeyDown)
|
||||
{
|
||||
WinKeyDown = true;
|
||||
if (!string.IsNullOrEmpty(shortcutString))
|
||||
{
|
||||
shortcutString += ", ";
|
||||
}
|
||||
shortcutString += "Win";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(shortcutString) && keyEvent.KeyCode != Keys.ControlKey && keyEvent.KeyCode != Keys.ShiftKey && keyEvent.KeyCode != Keys.Menu && keyEvent.KeyCode != Keys.LWin)
|
||||
{
|
||||
shortcutString += " + " + keyEvent.KeyCode.ToString();
|
||||
}
|
||||
|
||||
if (pressedModifiers == 0 && !WinKeyDown)
|
||||
{
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
}
|
||||
|
||||
return shortcutString;
|
||||
}
|
||||
|
||||
public static void HandleKeyboardShortcutUp(KeyEventArgs keyEvent)
|
||||
{
|
||||
if (keyEvent.KeyCode == Keys.LWin)
|
||||
{
|
||||
WinKeyDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void InitListView(ListView listView, IEnumerable<string> columns)
|
||||
{
|
||||
foreach (var name in columns)
|
||||
{
|
||||
var columnName = name;
|
||||
var parts = name.Split('|');
|
||||
|
||||
var width = 120;
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
width = int.Parse(parts[1]);
|
||||
columnName = parts[0];
|
||||
}
|
||||
|
||||
var header = listView.Columns.Add(columnName);
|
||||
header.Width = width == 120 ? -2 : width;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddOrUpdateListItem<T>(ListView listView, List<T> presets, Config config, T preset = null, ListViewItem specItem = null) where T : PresetBase
|
||||
{
|
||||
ListViewItem item = null;
|
||||
if (preset == null)
|
||||
{
|
||||
item = listView.SelectedItems[0];
|
||||
preset = (T)item.Tag;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = specItem;
|
||||
}
|
||||
|
||||
if (preset.id == 0)
|
||||
{
|
||||
preset.id = preset.GetHashCode();
|
||||
}
|
||||
|
||||
var values = preset.GetDisplayValues(config);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = listView.Items.Add(values[0]);
|
||||
item.Tag = preset;
|
||||
for (var i = 1; i < values.Count; i++)
|
||||
{
|
||||
item.SubItems.Add(values[i]);
|
||||
}
|
||||
if (!presets.Any(x => x.id == preset.id))
|
||||
{
|
||||
presets.Add(preset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Text = values[0];
|
||||
for (var i = 1; i < values.Count; i++)
|
||||
{
|
||||
item.SubItems[i].Text = values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
private static string FindIndexedProcessName(int pid)
|
||||
{
|
||||
var processName = Process.GetProcessById(pid).ProcessName;
|
||||
var processesByName = Process.GetProcessesByName(processName);
|
||||
string processIndexdName = null;
|
||||
|
||||
for (var index = 0; index < processesByName.Length; index++)
|
||||
{
|
||||
processIndexdName = index == 0 ? processName : processName + "#" + index;
|
||||
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
|
||||
if ((int)processId.NextValue() == pid)
|
||||
{
|
||||
return processIndexdName;
|
||||
}
|
||||
}
|
||||
|
||||
return processIndexdName;
|
||||
}
|
||||
|
||||
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
|
||||
{
|
||||
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
|
||||
return Process.GetProcessById((int)parentId.NextValue());
|
||||
}
|
||||
|
||||
public static Process Parent(this Process process)
|
||||
{
|
||||
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using PacketDotNet;
|
||||
using System.Net.NetworkInformation;
|
||||
using SharpPcap;
|
||||
using SharpPcap.Npcap;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
class WOL
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length.";
|
||||
private const string ArgumentExceptionInvalidPasswordLength = "Invalid password length.";
|
||||
private const int DefaultWolPort = 9;
|
||||
|
||||
public static bool WakeFunction(string macAddress, bool usePcap = false)
|
||||
{
|
||||
if (usePcap)
|
||||
{
|
||||
return WakeFunctionPcap(macAddress);
|
||||
}
|
||||
|
||||
return WakeFunctionToAllNics(macAddress);
|
||||
}
|
||||
|
||||
public static bool WakeFunctionToAllNics(string macAddress)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
try
|
||||
{
|
||||
var address = PhysicalAddress.Parse(macAddress);
|
||||
var addressBytes = address.GetAddressBytes();
|
||||
var data = GetWolPacket(addressBytes);
|
||||
|
||||
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach (var ni in interfaces)
|
||||
{
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.Name.Contains("VirtualBox"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Logger.Debug($"Found network: {ni.Name} ({ni.Description}), type: {ni.NetworkInterfaceType}");
|
||||
try
|
||||
{
|
||||
if (/*ni.OperationalStatus == OperationalStatus.Up &&*/ ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() != null)
|
||||
{
|
||||
foreach (var uip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (uip.Address.ToString().StartsWith("169.254"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (uip.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
Logger.Debug($"Broadcast WOL in network: {ni.Name} ({ni.Description}), local address: {uip.Address}");
|
||||
var local = new IPEndPoint(uip.Address, 0);
|
||||
var udpc = new UdpClient(local);
|
||||
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
|
||||
var target = new IPEndPoint(IPAddress.Broadcast, DefaultWolPort);
|
||||
udpc.Send(data, data.Length, target);
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"WakeFunctionToAllNics: while sending to specific network: {ni.Name} ({ni.Description}): {ex.ToLogString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"WakeFunctionToAllNics: {ex.ToLogString()}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
|
||||
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="password"/> is not 0 or 6.</exception>
|
||||
private static byte[] GetWolPacket(byte[] macAddress, byte[] password = null)
|
||||
{
|
||||
if (macAddress == null)
|
||||
throw new ArgumentNullException(nameof(macAddress));
|
||||
if (macAddress.Length != 6)
|
||||
throw new ArgumentException(ArgumentExceptionInvalidMacAddressLength);
|
||||
|
||||
password = password ?? new byte[0];
|
||||
if (password.Length != 0 && password.Length != 6)
|
||||
throw new ArgumentException(ArgumentExceptionInvalidPasswordLength);
|
||||
|
||||
var packet = new byte[17 * 6 + password.Length];
|
||||
|
||||
int offset, i;
|
||||
for (offset = 0; offset < 6; ++offset)
|
||||
packet[offset] = 0xFF;
|
||||
|
||||
for (offset = 6; offset < 17 * 6; offset += 6)
|
||||
for (i = 0; i < 6; ++i)
|
||||
packet[i + offset] = macAddress[i];
|
||||
|
||||
if (password.Length > 0)
|
||||
{
|
||||
for (offset = 16 * 6 + 6; offset < (17 * 6 + password.Length); offset += 6)
|
||||
for (i = 0; i < 6; ++i)
|
||||
packet[i + offset] = password[i];
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
//You need SharpPcap for this to work
|
||||
private static bool WakeFunctionPcap(string macAddress)
|
||||
{
|
||||
/* Retrieve the device list */
|
||||
var devices = CaptureDeviceList.Instance;
|
||||
|
||||
Logger.Debug("Waking device with MAC-address " + macAddress);
|
||||
|
||||
/*If no device exists, print error */
|
||||
if (devices.Count < 1)
|
||||
{
|
||||
Logger.Debug("No network device found on this machine");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
//if (device is NpcapDevice npcapDevice)
|
||||
//{
|
||||
// if (!npcapDevice.Interface.GatewayAddresses.Any())
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
//}
|
||||
|
||||
if (device is NpcapDevice npcapDevice)
|
||||
{
|
||||
Logger.Debug($"Opening network device (NpcapDevice): FriendlyName: {npcapDevice.Interface.FriendlyName}, Description: {npcapDevice.Interface.Description}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug($"Opening network device (NOT A NpcapDevice!): {device.Name}, Description: {device.Description}");
|
||||
}
|
||||
|
||||
//Open the device
|
||||
device.Open();
|
||||
|
||||
//A magic packet is a broadcast frame containing anywhere within its payload: 6 bytes of ones
|
||||
//(resulting in hexadecimal FF FF FF FF FF FF), followed by sixteen repetitions
|
||||
|
||||
string response = macAddress.Replace(":", "-");
|
||||
|
||||
var address = PhysicalAddress.Parse("FFFFFFFFFFFF");
|
||||
EthernetPacket ethernet = new EthernetPacket(address, address, EthernetType.WakeOnLan);
|
||||
ethernet.PayloadPacket = new WakeOnLanPacket(PhysicalAddress.Parse(response));
|
||||
|
||||
byte[] bytes = ethernet.Bytes;
|
||||
|
||||
try
|
||||
{
|
||||
//Send the packet out the network device
|
||||
device.SendPacket(bytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Debug(e.Message);
|
||||
}
|
||||
|
||||
device.Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using ColorControl;
|
||||
using ColorControl.Common;
|
||||
using System.IO;
|
||||
|
||||
namespace LgTv
|
||||
@@ -6,10 +6,12 @@ namespace LgTv
|
||||
public class ClientKeyStore
|
||||
{
|
||||
private string ip;
|
||||
|
||||
public ClientKeyStore(string ip)
|
||||
{
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public string GetClientKey()
|
||||
{
|
||||
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
|
||||
@@ -19,10 +21,34 @@ namespace LgTv
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SaveClientKey(string key)
|
||||
{
|
||||
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
|
||||
File.WriteAllText(filename, key);
|
||||
}
|
||||
|
||||
public bool HasValidHandShake(string expectedHandShake)
|
||||
{
|
||||
var handShake = GetHandShake();
|
||||
|
||||
return handShake?.Equals(expectedHandShake) ?? false;
|
||||
}
|
||||
|
||||
public string GetHandShake()
|
||||
{
|
||||
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
return File.ReadAllText(filename);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SaveHandShake(string data)
|
||||
{
|
||||
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
|
||||
File.WriteAllText(filename, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+537
-41
@@ -1,7 +1,8 @@
|
||||
using ColorControl;
|
||||
using ColorControl.Common;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -12,13 +13,263 @@ namespace LgTv
|
||||
|
||||
public enum ControlButtons
|
||||
{
|
||||
Back,Down,Left,Right,
|
||||
Back, Down, Left, Right,
|
||||
OK,
|
||||
Exit
|
||||
}
|
||||
public class LgTvApi:IDisposable
|
||||
|
||||
public enum PictureMode
|
||||
{
|
||||
public bool ConnectionClosed { get => _connection == null || _connection.ConnectionClosed; }
|
||||
cinema, eco, expert1, expert2, game, normal, photo, sports, technicolor, vivid, hdrEffect, filmMaker,
|
||||
|
||||
hdrCinema, hdrCinemaBright, hdrExternal, hdrGame, hdrStandard, hdrTechnicolor, hdrVivid, hdrFilmMaker,
|
||||
|
||||
dolbyHdrCinema, dolbyHdrCinemaBright, dolbyHdrDarkAmazon, dolbyHdrGame, dolbyHdrStandard, dolbyHdrVivid, dolbyStandard
|
||||
}
|
||||
|
||||
public enum DynamicRange
|
||||
{
|
||||
sdr, hdr, technicolorHdr, dolbyHdr,
|
||||
sdrALLM, hdrALLM, technicolorHdrALLM, dolbyHdrALLM
|
||||
}
|
||||
|
||||
public enum OffToHigh
|
||||
{
|
||||
off,
|
||||
low,
|
||||
medium,
|
||||
high
|
||||
}
|
||||
|
||||
public enum LowToAuto
|
||||
{
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum OffToOn
|
||||
{
|
||||
off,
|
||||
on
|
||||
}
|
||||
|
||||
public enum OffToAuto2
|
||||
{
|
||||
off,
|
||||
on,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum OffToAuto
|
||||
{
|
||||
off,
|
||||
on,
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum ColorGamut
|
||||
{
|
||||
auto,
|
||||
extended,
|
||||
wide,
|
||||
srgb,
|
||||
native
|
||||
}
|
||||
|
||||
public enum EnergySaving
|
||||
{
|
||||
auto,
|
||||
off,
|
||||
min,
|
||||
med,
|
||||
max,
|
||||
screen_off
|
||||
}
|
||||
|
||||
public enum DynamicTonemapping
|
||||
{
|
||||
on,
|
||||
off,
|
||||
HGIG,
|
||||
}
|
||||
|
||||
public enum TruMotionMode
|
||||
{
|
||||
off,
|
||||
smooth,
|
||||
clear,
|
||||
clearPlus,
|
||||
user
|
||||
}
|
||||
|
||||
public enum WhiteBalanceColorTemperature
|
||||
{
|
||||
cool,
|
||||
medium,
|
||||
warm1,
|
||||
warm2,
|
||||
warm3
|
||||
}
|
||||
|
||||
public enum FalseToTrue
|
||||
{
|
||||
[Description("Disabled")]
|
||||
false_,
|
||||
[Description("Enabled")]
|
||||
true_
|
||||
}
|
||||
|
||||
public enum BoolFalseToTrue
|
||||
{
|
||||
[Description("Disabled")]
|
||||
bool_false,
|
||||
[Description("Enabled")]
|
||||
bool_true
|
||||
}
|
||||
|
||||
public enum GammaExp
|
||||
{
|
||||
low,
|
||||
medium,
|
||||
high1,
|
||||
high2
|
||||
}
|
||||
|
||||
public enum BlackLevel
|
||||
{
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
limited,
|
||||
full,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum AspectRatio
|
||||
{
|
||||
_21x9,
|
||||
_16x9,
|
||||
_4x3,
|
||||
_14x9,
|
||||
_32x9,
|
||||
_32x12,
|
||||
just_scan,
|
||||
original,
|
||||
full_wide,
|
||||
limited,
|
||||
zoom,
|
||||
zoom2,
|
||||
cinema_zoom,
|
||||
vertZoom,
|
||||
allDirZoom,
|
||||
twinZoom
|
||||
}
|
||||
|
||||
public enum InputOptimization
|
||||
{
|
||||
auto,
|
||||
on,
|
||||
standard,
|
||||
boost
|
||||
}
|
||||
|
||||
public enum BlueLight
|
||||
{
|
||||
off,
|
||||
level1,
|
||||
level2
|
||||
}
|
||||
|
||||
public enum HdmiIcon
|
||||
{
|
||||
[Description("HDMI")]
|
||||
hdmigeneric,
|
||||
[Description("Satellite")]
|
||||
satellite,
|
||||
[Description("Set-Top Box")]
|
||||
settopbox,
|
||||
[Description("DVD Player")]
|
||||
dvd,
|
||||
[Description("Blu-ray Player")]
|
||||
bluray,
|
||||
[Description("Home Theater")]
|
||||
hometheater,
|
||||
[Description("Game Console")]
|
||||
gameconsole,
|
||||
[Description("Streaming Box")]
|
||||
streamingbox,
|
||||
[Description("Generic Camera")]
|
||||
camera,
|
||||
[Description("PC")]
|
||||
pc,
|
||||
[Description("Mobile Device")]
|
||||
mobile
|
||||
}
|
||||
|
||||
public enum SoundMode
|
||||
{
|
||||
[Description("AI Sound Pro")]
|
||||
aiSoundPlus,
|
||||
[Description("AI Sound")]
|
||||
aiSound,
|
||||
[Description("Standard")]
|
||||
standard,
|
||||
[Description("Clear Voice")]
|
||||
news,
|
||||
[Description("Music")]
|
||||
music,
|
||||
[Description("Cinema")]
|
||||
movie,
|
||||
[Description("Sports")]
|
||||
sports,
|
||||
[Description("Game Optimizer")]
|
||||
game,
|
||||
[Description("Pagode")]
|
||||
pagode,
|
||||
[Description("Serta Wego")]
|
||||
sertaWego,
|
||||
[Description("Brazilian Punk")]
|
||||
brazilianPunk,
|
||||
[Description("ASC")]
|
||||
asc,
|
||||
[Description("Bass Boost")]
|
||||
bass,
|
||||
}
|
||||
|
||||
public enum SoundOutput
|
||||
{
|
||||
[Description("TV Speaker")]
|
||||
tv_speaker,
|
||||
[Description("HDMI(ARC) Device")]
|
||||
external_arc,
|
||||
[Description("Optical Out Device")]
|
||||
external_optical,
|
||||
[Description("Bluetooth Device")]
|
||||
bt_soundbar,
|
||||
[Description("Mobile Device")]
|
||||
mobile_phone,
|
||||
[Description("Audio Out Device")]
|
||||
lineout,
|
||||
[Description("Wired Headphones")]
|
||||
headphone,
|
||||
[Description("Bluetooth Device + TV Speaker")]
|
||||
tv_speaker_bluetooth,
|
||||
[Description("Optical Out Device + TV Speaker")]
|
||||
tv_external_speaker,
|
||||
[Description("Wired Headphones + TV Speaker")]
|
||||
tv_speaker_headphone,
|
||||
[Description("WiSA Speakers")]
|
||||
wisa_speaker,
|
||||
}
|
||||
|
||||
public class LgTvApi : IDisposable
|
||||
{
|
||||
public bool ConnectionClosed { get => _connection?.ConnectionClosed ?? true; }
|
||||
|
||||
private readonly LgTvApiCore _connection;
|
||||
private LgWebOsMouseService _mouseConnection;
|
||||
@@ -28,18 +279,7 @@ namespace LgTv
|
||||
private List<Channel> _channelList;
|
||||
private List<App> _appList;
|
||||
private string _ip;
|
||||
|
||||
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
|
||||
|
||||
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
|
||||
|
||||
public Task ControlButton(object oK)
|
||||
{
|
||||
// throw new NotImplementedException();
|
||||
return null;
|
||||
}
|
||||
|
||||
private string _currentPariKey;
|
||||
private string _currentPairKey;
|
||||
private string webSocketUri;
|
||||
|
||||
public static async Task<LgTvApi> CreateLgTvApi(string ip, int retries = 1)
|
||||
@@ -75,23 +315,27 @@ namespace LgTv
|
||||
|
||||
public async Task MakeHandShake()
|
||||
{
|
||||
_currentPariKey = _keyStore.GetClientKey();
|
||||
if (_currentPariKey != null)
|
||||
var registerJson = Utils.GetResourceFile("LG_register.json");
|
||||
|
||||
_currentPairKey = _keyStore.GetClientKey();
|
||||
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(registerJson))
|
||||
{
|
||||
var key = AFTER_PAIR_HAND_SHAKE.Replace("CLIENTKEYGOESHERE", _currentPariKey);
|
||||
var key = registerJson.Replace("CLIENTKEYGOESHERE", _currentPairKey);
|
||||
var conn = await _connection.SendCommandAsync(key);
|
||||
_keyStore.SaveClientKey((string)conn.clientKey);
|
||||
return;
|
||||
}
|
||||
|
||||
dynamic result = await _connection.SendCommandAsync(BEFORE_PAIR_HAND_SHAKE);
|
||||
var registerJsonRemovedKey = registerJson.Replace("CLIENTKEYGOESHERE", string.Empty);
|
||||
dynamic result = await _connection.SendCommandAsync(registerJsonRemovedKey);
|
||||
_keyStore.SaveClientKey(result.clientKey);
|
||||
_keyStore.SaveHandShake(registerJson);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<bool> Connect()
|
||||
{
|
||||
var ctx = _connection.Connect(new Uri(webSocketUri));
|
||||
var ctx = _connection.Connect(new Uri(webSocketUri));
|
||||
return await ctx;
|
||||
}
|
||||
|
||||
@@ -101,7 +345,7 @@ namespace LgTv
|
||||
var requestMessage = new RequestMessage("ssap://com.webos.service.networkinput/getPointerInputSocket", new { });
|
||||
var s = await _connection.SendCommandAsync(requestMessage);
|
||||
var socketPath = (string)s.socketPath;
|
||||
|
||||
|
||||
_mouseConnection = new LgWebOsMouseService(new LgTvApiCore());
|
||||
await _mouseConnection.Connect(socketPath);
|
||||
return _mouseConnection;
|
||||
@@ -135,10 +379,10 @@ namespace LgTv
|
||||
}
|
||||
public async Task ShowToast()
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://system.notifications/createToast",new {message = "Co tam u Ciebie?"});
|
||||
var requestMessage = new RequestMessage("ssap://system.notifications/createToast", new { message = "Co tam u Ciebie?" });
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
|
||||
|
||||
//public async Task<string> LaunchApp(string appId,Uri uri)
|
||||
//{
|
||||
// var url = uri.ToString();
|
||||
@@ -152,7 +396,7 @@ namespace LgTv
|
||||
var requestMessage = new RequestMessage("volumedown", "ssap://audio/volumeDown");
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
public async Task VolumeUp()
|
||||
public async Task VolumeUp()
|
||||
{
|
||||
var requestMessage = new RequestMessage("volumeup", "ssap://audio/volumeUp");
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
@@ -163,12 +407,12 @@ namespace LgTv
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
|
||||
public async Task SetChannel(string channelId)
|
||||
public async Task SetChannel(string channelId)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://tv/openChannel", new { channelId });
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
public async Task SetMute(bool value)
|
||||
public async Task SetMute(bool value)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://audio/setMute", new { mute = value });
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
@@ -184,7 +428,7 @@ namespace LgTv
|
||||
return (bool)(await command).mute;
|
||||
}
|
||||
|
||||
public async Task SetVolume(int value)
|
||||
public async Task SetVolume(int value)
|
||||
{
|
||||
if (value >= 0 && value <= 100)
|
||||
{
|
||||
@@ -192,11 +436,16 @@ namespace LgTv
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
}
|
||||
public async Task TurnOff()
|
||||
public async Task TurnOff()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("","ssap://system/turnOff"));
|
||||
await _connection.SendCommandAsync(new RequestMessage("", "ssap://system/turnOff"));
|
||||
}
|
||||
public async Task Play()
|
||||
public async Task Reboot()
|
||||
{
|
||||
await ExecuteRequest("luna://com.webos.service.tv.power/reboot", new { reason = "" });
|
||||
}
|
||||
|
||||
public async Task Play()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("play", "ssap://media.controls/play"));
|
||||
}
|
||||
@@ -214,7 +463,7 @@ namespace LgTv
|
||||
await _connection.SendCommandAsync(new RequestMessage("channelDown", "ssap://tv/channelDown"));
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
public async Task Stop()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("stop", "ssap://media.controls/stop"));
|
||||
}
|
||||
@@ -227,12 +476,23 @@ namespace LgTv
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("3d", "ssap://com.webos.service.tv.display/set3DOff"));
|
||||
}
|
||||
|
||||
public async Task TurnScreenOff()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOffScreen"));
|
||||
}
|
||||
|
||||
public async Task TurnScreenOn()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOnScreen"));
|
||||
}
|
||||
|
||||
public async Task<bool> IsTurnedOn3D()
|
||||
{
|
||||
//Response: { returnValue: true, status3D: { status: true, pattern: ’2Dto3D’ } }
|
||||
var requestMessage = new RequestMessage("status3D", "ssap://com.webos.service.tv.display/get3DStatus");
|
||||
var o = await _connection.SendCommandAsync(requestMessage);
|
||||
return (bool)o.status3D.status;
|
||||
return (bool)o.status3D.status;
|
||||
}
|
||||
|
||||
public async Task<int> GetVolume()
|
||||
@@ -252,12 +512,12 @@ namespace LgTv
|
||||
// }
|
||||
var requestMessage = new RequestMessage("status", "ssap://audio/getVolume");
|
||||
var o = await _connection.SendCommandAsync(requestMessage);
|
||||
return( (bool)o.muted) ? -1 :(int) o.volume;
|
||||
return ((bool)o.muted) ? -1 : (int)o.volume;
|
||||
}
|
||||
public async Task<IEnumerable<ExternalInput>> GetInputList()
|
||||
{
|
||||
var requestMessage = new RequestMessage("input","ssap://tv/getExternalInputList");
|
||||
var results = await _connection.SendCommandAsync(requestMessage);
|
||||
var requestMessage = new RequestMessage("input", "ssap://tv/getExternalInputList");
|
||||
var results = await _connection.SendCommandAsync(requestMessage);
|
||||
var l = new List<ExternalInput>();
|
||||
foreach (var result in results)
|
||||
{
|
||||
@@ -269,12 +529,49 @@ namespace LgTv
|
||||
return l;
|
||||
}
|
||||
|
||||
public async Task SetInput(string id)
|
||||
public async Task SetInput(string id)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://tv/switchInput", new { inputId = id });
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
|
||||
public async Task SubscribeVolume(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://audio/getVolume", null, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task SubscribePowerState(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://com.webos.service.tvpower/power/getPowerState", null, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task SubscribeForegroundApp(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://com.webos.applicationManager/getForegroundAppInfo", null, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task SubscribePictureSettings(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
var keys = @"[ ""contrast"", ""backlight"", ""brightness"", ""color"" ]";
|
||||
|
||||
var json = @"{ ""category"": ""picture"", ""keys"": " + keys + @", ""subscribe"": true }";
|
||||
|
||||
payload = JObject.Parse(json);
|
||||
}
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<App>> GetLaunchPoints()
|
||||
{
|
||||
_appList = new List<App>();
|
||||
@@ -333,9 +630,19 @@ namespace LgTv
|
||||
return appList.OrderBy(e => e.title);
|
||||
}
|
||||
|
||||
public async Task<string> LaunchApp(string appId)
|
||||
public async Task<string> LaunchApp(string appId, dynamic @params = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://system.launcher/launch", new { id = appId });
|
||||
dynamic payload;
|
||||
if (@params != null)
|
||||
{
|
||||
payload = new { id = appId, @params = @params };
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = new { id = appId };
|
||||
}
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://system.launcher/launch", payload);
|
||||
var response = await _connection.SendCommandAsync(requestMessage);
|
||||
return (string)response.sessionId;
|
||||
}
|
||||
@@ -365,6 +672,195 @@ namespace LgTv
|
||||
return (string)response.sessionId;
|
||||
}
|
||||
|
||||
public async Task SetSystemSettings(string key, object value, string category = "picture")
|
||||
{
|
||||
var jsonValue = ParamToJson(value, ref key);
|
||||
|
||||
var lunauri = "luna://com.webos.settingsservice/setSystemSettings";
|
||||
|
||||
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" } }";
|
||||
|
||||
var @params = JObject.Parse(json);
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
private static string ParamToJson(object value, ref string key, bool skipSeparator = false)
|
||||
{
|
||||
string jsonValue;
|
||||
var valueType = value.GetType();
|
||||
var intType = typeof(int);
|
||||
if (value.ToString().StartsWith("bool_"))
|
||||
{
|
||||
value = bool.Parse(value.ToString().Substring(5));
|
||||
}
|
||||
|
||||
if (valueType.IsArray && intType.IsAssignableFrom(valueType.GetElementType()))
|
||||
{
|
||||
var values = ((Array)value).Cast<int>();
|
||||
jsonValue = "[" + string.Join(", ", values.Select(x => x.ToString()).ToArray()) + ']';
|
||||
}
|
||||
else if (key?.Contains("_") == true && !skipSeparator)
|
||||
{
|
||||
var keys = key.Split('_');
|
||||
key = keys[0];
|
||||
var childKey = keys[1];
|
||||
|
||||
if (value is string strValue && strValue != "false" && strValue != "true" && Utils.ParseInt(strValue, int.MinValue) == int.MinValue)
|
||||
{
|
||||
value = $"\"{value}\"";
|
||||
}
|
||||
|
||||
jsonValue = @"{ """ + childKey + @""": " + value + " }";
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
jsonValue = value.ToString().ToLowerInvariant();
|
||||
}
|
||||
else if (key.Equals("blackLevel", StringComparison.Ordinal))
|
||||
{
|
||||
jsonValue = @"{
|
||||
""ntsc"": ""auto"",
|
||||
""ntsc443"": ""auto"",
|
||||
""pal"": ""auto"",
|
||||
""pal60"": ""auto"",
|
||||
""palm"": ""auto"",
|
||||
""paln"": ""auto"",
|
||||
""secam"": ""auto"",
|
||||
""unknown"": ""auto""
|
||||
}";
|
||||
|
||||
jsonValue = jsonValue.Replace("auto", value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key.Equals("arcPerApp") && value.ToString().StartsWith("_"))
|
||||
{
|
||||
value = value.ToString().Substring(1);
|
||||
}
|
||||
|
||||
jsonValue = $"\"{value}\"";
|
||||
}
|
||||
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
public async Task SetConfig(string key, object value)
|
||||
{
|
||||
var lunauri = "luna://com.webos.service.config/setConfigs";
|
||||
|
||||
var jsonValue = ParamToJson(value, ref key);
|
||||
|
||||
var @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": " + jsonValue + @" } }");
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task SetSystemProperty(string key, object value)
|
||||
{
|
||||
var lunauri = "luna://com.webos.service.tv.systemproperty/setProperties";
|
||||
|
||||
var jsonValue = ParamToJson(value, ref key, true);
|
||||
|
||||
//var @params = JObject.Parse(@"{ ""keys"": { """ + key + @""": " + jsonValue + @" } }");
|
||||
var @params = JObject.Parse(@"{ """ + key + @""": " + jsonValue + @" }");
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task SetDeviceConfig(string id, string icon, string label)
|
||||
{
|
||||
var lunauri = "luna://com.webos.service.eim/setDeviceInfo";
|
||||
|
||||
var iconPng = icon + ".png";
|
||||
|
||||
var @params = new
|
||||
{
|
||||
id = id,
|
||||
label = label,
|
||||
icon = iconPng
|
||||
};
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
private async Task ExecuteRequest(string lunauri, object @params)
|
||||
{
|
||||
var buttons = new[]
|
||||
{
|
||||
new {
|
||||
label = "",
|
||||
onClick = lunauri,
|
||||
@params = @params
|
||||
}
|
||||
};
|
||||
|
||||
var payload = new
|
||||
{
|
||||
message = "Applying...",
|
||||
buttons = buttons,
|
||||
onclose = new { uri = lunauri, @params = @params },
|
||||
onfail = new { uri = lunauri, @params = @params }
|
||||
};
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://system.notifications/createAlert", payload);
|
||||
var response = await _connection.SendCommandAsync(requestMessage);
|
||||
|
||||
var alertId = (string)response.alertId;
|
||||
if (alertId != null)
|
||||
{
|
||||
var closeAlert = new
|
||||
{
|
||||
alertId = alertId
|
||||
};
|
||||
requestMessage = new RequestMessage("ssap://system.notifications/closeAlert", closeAlert);
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemInfo(params string[] keys)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
keys = keys
|
||||
};
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://system/getSystemInfo", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemSettings(string category, params string[] keys)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
category = category,
|
||||
keys = keys
|
||||
};
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemSettings2(string category)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
category = "dimensionInfo",
|
||||
//dimension = new { input = "default", _3dStatus = "2d", dynamicRange = "sdr" },
|
||||
keys = new[] { "input", "_3dStatus", "colorSystem", "dynamicRange", "gameInput" }
|
||||
};
|
||||
//await ExecuteRequest("luna://com.webos.settingsservice/getSystemSettings", payload);
|
||||
//return null;
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
@@ -372,7 +868,7 @@ namespace LgTv
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_connection?.Dispose();
|
||||
_connection?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using ColorControl.Common;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.Web;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace LgTv
|
||||
{
|
||||
@@ -18,7 +18,7 @@ namespace LgTv
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class LgTvApiCore : IDisposable
|
||||
{
|
||||
public bool ConnectionClosed { get; private set; }
|
||||
@@ -29,6 +29,7 @@ namespace LgTv
|
||||
|
||||
public event IsConnectedDelegate IsConnected;
|
||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<dynamic>> _tokens = new ConcurrentDictionary<string, TaskCompletionSource<dynamic>>();
|
||||
private readonly ConcurrentDictionary<string, Func<dynamic, bool>> _callbacks = new ConcurrentDictionary<string, Func<dynamic, bool>>();
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
@@ -40,16 +41,22 @@ namespace LgTv
|
||||
_commandCount = 0;
|
||||
_connection = new MessageWebSocket();
|
||||
_connection.Control.MessageType = SocketMessageType.Utf8;
|
||||
// if(ignoreReceiver==false)
|
||||
_connection.MessageReceived += Connection_MessageReceived;
|
||||
|
||||
_connection.MessageReceived += Connection_MessageReceived;
|
||||
_connection.Closed += Connection_Closed;
|
||||
await _connection.ConnectAsync(uri);
|
||||
|
||||
IsConnected?.Invoke(true);
|
||||
try
|
||||
{
|
||||
await _connection.ConnectAsync(uri).AsTask().WaitAsync(TimeSpan.FromSeconds(5));
|
||||
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
return true;
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
|
||||
IsConnected?.Invoke(true);
|
||||
return true;
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -66,12 +73,27 @@ namespace LgTv
|
||||
}
|
||||
}
|
||||
|
||||
public async void SendMessageAsync(string message)
|
||||
public async Task SendMessageAsync(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_messageWriter == null)
|
||||
{
|
||||
throw new Exception("SendMessageAsync: no writer");
|
||||
}
|
||||
|
||||
_messageWriter.WriteString(message);
|
||||
await _messageWriter.StoreAsync();
|
||||
|
||||
using (var cancellationTokenSource = new CancellationTokenSource(5000))
|
||||
{
|
||||
await _messageWriter.StoreAsync().AsTask(cancellationTokenSource.Token).ContinueWith((antecedent) =>
|
||||
{
|
||||
if (antecedent.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
throw new Exception("SendMessageAsync cancelled due to timeout");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -81,24 +103,25 @@ namespace LgTv
|
||||
}
|
||||
}
|
||||
|
||||
public Task<dynamic> SendCommandAsync(string message)
|
||||
public async Task<dynamic> SendCommandAsync(string message)
|
||||
{
|
||||
var obj = JsonConvert.DeserializeObject<dynamic>(message);
|
||||
return SendCommandAsync((string)obj.id, message);
|
||||
return await SendCommandAsync((string)obj.id, message);
|
||||
}
|
||||
|
||||
private Task<dynamic> SendCommandAsync(string id, string message)
|
||||
private async Task<dynamic> SendCommandAsync(string id, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var taskSource = new TaskCompletionSource<dynamic>();
|
||||
_tokens.TryAdd(id, taskSource);
|
||||
SendMessageAsync(message);
|
||||
await SendMessageAsync(message);
|
||||
if (ConnectionClosed)
|
||||
{
|
||||
throw new Exception("Connection closed");
|
||||
}
|
||||
return taskSource.Task;
|
||||
|
||||
return await taskSource.Task;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -106,7 +129,7 @@ namespace LgTv
|
||||
}
|
||||
}
|
||||
|
||||
public Task<dynamic> SendCommandAsync(RequestMessage message)
|
||||
public async Task<dynamic> SendCommandAsync(RequestMessage message)
|
||||
{
|
||||
var rawMessage = new RawRequestMessage(message, ++_commandCount);
|
||||
var serialized = JsonConvert.SerializeObject(rawMessage, new JsonSerializerSettings()
|
||||
@@ -114,7 +137,21 @@ namespace LgTv
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
});
|
||||
return SendCommandAsync(rawMessage.Id, serialized);
|
||||
return await SendCommandAsync(rawMessage.Id, serialized);
|
||||
}
|
||||
|
||||
public async Task<dynamic> SubscribeAsync(RequestMessage message, Func<dynamic, bool> callback)
|
||||
{
|
||||
var rawMessage = new RawRequestMessage(message, ++_commandCount);
|
||||
var serialized = JsonConvert.SerializeObject(rawMessage, new JsonSerializerSettings()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
});
|
||||
|
||||
_callbacks.TryAdd(rawMessage.Id, callback);
|
||||
|
||||
return await SendCommandAsync(rawMessage.Id, serialized);
|
||||
}
|
||||
|
||||
private void Connection_Closed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
@@ -128,7 +165,17 @@ namespace LgTv
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var dr = args.GetDataReader())
|
||||
var task = new Task<DataReader>(new Func<DataReader>(args.GetDataReader));
|
||||
task.Start();
|
||||
var result = task.Wait(5000);
|
||||
|
||||
var dr = result ? task.Result : null;
|
||||
if (!result)
|
||||
{
|
||||
throw new Exception("Timeout while reading response, possible disconnect");
|
||||
}
|
||||
|
||||
using (dr)
|
||||
{
|
||||
dr.UnicodeEncoding = UnicodeEncoding.Utf8;
|
||||
var message = dr.ReadString(dr.UnconsumedBufferLength);
|
||||
@@ -146,30 +193,68 @@ namespace LgTv
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (_tokens.TryGetValue(id, out taskCompletion))
|
||||
else if (_tokens.TryGetValue(id, out taskCompletion))
|
||||
{
|
||||
if (id == "register_0") return;
|
||||
if (obj.type == "error")
|
||||
{
|
||||
taskCompletion.SetException(new Exception(obj.error));
|
||||
taskCompletion.SetException(new Exception(obj.error?.ToString()));
|
||||
}
|
||||
//else if (args.Cancelled)
|
||||
//{
|
||||
// taskSource.SetCanceled();
|
||||
//}
|
||||
taskCompletion.TrySetResult(obj.payload);
|
||||
|
||||
if (_callbacks.TryGetValue(id, out Func<dynamic, bool> callback))
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(obj.payload);
|
||||
}
|
||||
catch (Exception callbackException)
|
||||
{
|
||||
Logger.Error($"Connection_MessageReceived: the callback threw an exception: {callbackException.ToLogString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.Message}");
|
||||
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.ToLogString()}");
|
||||
|
||||
SetExceptionOnAllTokens(ex);
|
||||
|
||||
ConnectionClosed = true;
|
||||
_messageWriter?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetExceptionOnAllTokens(Exception ex)
|
||||
{
|
||||
//Logger.Debug($"Tokens: {_tokens.Count}");
|
||||
|
||||
foreach (var taskCompletion in _tokens.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (taskCompletion.Task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
Logger.Debug("taskCompletion.Task.Status: " + taskCompletion.Task.Status);
|
||||
taskCompletion.SetException(new Exception(ex.Message));
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
Logger.Error("SetExceptionOnAllTokens: " + ex2.Message);
|
||||
}
|
||||
}
|
||||
|
||||
_tokens.Clear();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_connection?.Close(1, "");
|
||||
|
||||
@@ -33,7 +33,29 @@ namespace LgTv
|
||||
MUTE,
|
||||
MENU,
|
||||
CC,
|
||||
DASH
|
||||
DASH,
|
||||
CHANNELUP,
|
||||
CHANNELDOWN,
|
||||
LIST,
|
||||
AD,
|
||||
SAP,
|
||||
PROGRAM,
|
||||
PLAY,
|
||||
PAUSE,
|
||||
STOP,
|
||||
REWIND,
|
||||
FASTFORWARD,
|
||||
GUIDE,
|
||||
AMAZON,
|
||||
NETFLIX,
|
||||
MAGNIFIER_ZOOM,
|
||||
LIVE_ZOOM,
|
||||
_3D_MODE,
|
||||
ASPECT_RATIO,
|
||||
RECENT,
|
||||
RECORD,
|
||||
SCREEN_REMOTE,
|
||||
MYAPPS
|
||||
}
|
||||
|
||||
public class LgWebOsMouseService : IDisposable
|
||||
@@ -51,7 +73,7 @@ namespace LgTv
|
||||
}
|
||||
public void SendButton(int number)
|
||||
{
|
||||
_connection.SendMessageAsync($"type:button\nname:{number}\n\n");
|
||||
_connection.SendMessageAsync($"type:button\nname:{number}\n\n").ConfigureAwait(false);
|
||||
}
|
||||
public void SendButton(ButtonType bt)
|
||||
{
|
||||
@@ -60,23 +82,23 @@ namespace LgTv
|
||||
{
|
||||
text = text.Substring(1);
|
||||
}
|
||||
_connection.SendMessageAsync($"type:button\nname:{text}\n\n");
|
||||
_connection.SendMessageAsync($"type:button\nname:{text}\n\n").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
public void Move(double dx, double dy, bool drag = false)
|
||||
{
|
||||
_connection.SendMessageAsync($"type:move\ndx:{dx}\ndy:{dy}\ndown:{(drag ? 1 : 0)}\n\n");
|
||||
_connection.SendMessageAsync($"type:move\ndx:{dx}\ndy:{dy}\ndown:{(drag ? 1 : 0)}\n\n").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Scroll(double dx, double dy)
|
||||
{
|
||||
_connection.SendMessageAsync($"type:scroll\ndx:{dx}\ndy:{dy}\n\n");
|
||||
_connection.SendMessageAsync($"type:scroll\ndx:{dx}\ndy:{dy}\n\n").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Click()
|
||||
{
|
||||
_connection.SendMessageAsync("type:click\n\n");
|
||||
_connection.SendMessageAsync("type:click\n\n").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -4,7 +4,7 @@ LG TV WebOS API for .NET
|
||||
For now, there is only API, example app will be added in the future.
|
||||
|
||||
## inspiration:
|
||||
* https://github.com/msloth/lgtv.js/blob/master/index.js
|
||||
* https://github.com/msloth/lgtv.js/blob/master/lgtv.js
|
||||
|
||||
## references:
|
||||
* https://github.com/ConnectSDK/Connect-SDK-Android-Core
|
||||
|
||||
@@ -9,10 +9,14 @@ namespace LgTv
|
||||
Prefix = prefix;
|
||||
Uri = uri;
|
||||
}
|
||||
public RequestMessage(string uri,object payload)
|
||||
public RequestMessage(string uri, object payload, string type = "request")
|
||||
{
|
||||
Uri = uri;
|
||||
Payload = JsonConvert.SerializeObject(payload);
|
||||
if (payload != null)
|
||||
{
|
||||
Payload = JsonConvert.SerializeObject(payload);
|
||||
}
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Native" version="0.1.0" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
<package id="NLog" version="4.7.9" targetFramework="net472" />
|
||||
<package id="NStandard" version="0.6.12.5" targetFramework="net472" />
|
||||
<package id="NvAPIWrapper.Net" version="0.8.1.101" targetFramework="net472" />
|
||||
<package id="NWin32" version="1.0.7.6" targetFramework="net472" />
|
||||
<package id="PacketDotNet" version="1.2.0" targetFramework="net472" />
|
||||
<package id="SharpPcap" version="5.4.0" targetFramework="net472" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
|
||||
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net472" />
|
||||
<package id="TaskScheduler" version="2.9.1" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
internal class CachedSettingValue
|
||||
{
|
||||
|
||||
internal CachedSettingValue() { }
|
||||
|
||||
internal CachedSettingValue(uint Value, string ProfileNames)
|
||||
{
|
||||
this.Value = Value;
|
||||
this.ProfileNames = new StringBuilder(ProfileNames);
|
||||
this.ValueProfileCount = 1;
|
||||
}
|
||||
|
||||
internal CachedSettingValue(string ValueStr, string ProfileNames)
|
||||
{
|
||||
this.ValueStr = ValueStr;
|
||||
this.ProfileNames = new StringBuilder(ProfileNames);
|
||||
this.ValueProfileCount = 1;
|
||||
}
|
||||
|
||||
internal CachedSettingValue(byte[] ValueBin, string ProfileNames)
|
||||
{
|
||||
this.ValueBin = ValueBin;
|
||||
this.ProfileNames = new StringBuilder(ProfileNames);
|
||||
this.ValueProfileCount = 1;
|
||||
}
|
||||
|
||||
internal string ValueStr = "";
|
||||
internal uint Value = 0;
|
||||
internal byte[] ValueBin = new byte[0];
|
||||
internal StringBuilder ProfileNames;
|
||||
internal uint ValueProfileCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
internal class CachedSettings
|
||||
{
|
||||
internal CachedSettings() { }
|
||||
|
||||
internal CachedSettings(uint settingId, NVDRS_SETTING_TYPE settingType)
|
||||
{
|
||||
SettingId = settingId;
|
||||
SettingType = settingType;
|
||||
}
|
||||
|
||||
internal uint SettingId;
|
||||
|
||||
internal List<CachedSettingValue> SettingValues = new List<CachedSettingValue>();
|
||||
|
||||
internal uint ProfileCount = 0;
|
||||
|
||||
internal NVDRS_SETTING_TYPE SettingType = NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE;
|
||||
|
||||
internal void AddDwordValue(uint valueDword, string Profile)
|
||||
{
|
||||
var setting = SettingValues.FirstOrDefault(s => s.Value == valueDword);
|
||||
if (setting == null)
|
||||
{
|
||||
SettingValues.Add(new CachedSettingValue(valueDword, Profile));
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.ProfileNames.Append(", " + Profile);
|
||||
setting.ValueProfileCount++;
|
||||
}
|
||||
ProfileCount++;
|
||||
}
|
||||
|
||||
internal void AddStringValue(string valueStr, string Profile)
|
||||
{
|
||||
|
||||
var setting = SettingValues.FirstOrDefault(s => s.ValueStr == valueStr);
|
||||
if (setting == null)
|
||||
{
|
||||
SettingValues.Add(new CachedSettingValue(valueStr, Profile));
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.ProfileNames.Append(", " + Profile);
|
||||
setting.ValueProfileCount++;
|
||||
}
|
||||
ProfileCount++;
|
||||
}
|
||||
|
||||
internal void AddBinaryValue(byte[] valueBin, string Profile)
|
||||
{
|
||||
|
||||
var setting = SettingValues.FirstOrDefault(s => s.ValueBin.SequenceEqual(valueBin));
|
||||
if (setting == null)
|
||||
{
|
||||
SettingValues.Add(new CachedSettingValue(valueBin, Profile));
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.ProfileNames.Append(", " + Profile);
|
||||
setting.ValueProfileCount++;
|
||||
}
|
||||
ProfileCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace nspector.Common.CustomSettings
|
||||
{
|
||||
[Serializable]
|
||||
public class CustomSetting
|
||||
{
|
||||
|
||||
public string UserfriendlyName { get; set; }
|
||||
[XmlElement(ElementName = "HexSettingID")]
|
||||
public string HexSettingId { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public string OverrideDefault { get; set; }
|
||||
public float MinRequiredDriverVersion { get; set; }
|
||||
|
||||
public List<CustomSettingValue> SettingValues { get; set; }
|
||||
|
||||
internal uint SettingId
|
||||
{
|
||||
get { return Convert.ToUInt32(HexSettingId.Trim(), 16); }
|
||||
}
|
||||
|
||||
internal uint? DefaultValue
|
||||
{
|
||||
get { return string.IsNullOrEmpty(OverrideDefault) ? null : (uint?)Convert.ToUInt32(OverrideDefault.Trim(), 16); }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using nspector.Common.Helper;
|
||||
|
||||
namespace nspector.Common.CustomSettings
|
||||
{
|
||||
[Serializable]
|
||||
public class CustomSettingNames
|
||||
{
|
||||
public List<CustomSetting> Settings = new List<CustomSetting>();
|
||||
|
||||
public void StoreToFile(string filename)
|
||||
{
|
||||
XMLHelper<CustomSettingNames>.SerializeToXmlFile(this, filename, Encoding.Unicode, true);
|
||||
}
|
||||
|
||||
public static CustomSettingNames FactoryLoadFromFile(string filename)
|
||||
{
|
||||
return XMLHelper<CustomSettingNames>.DeserializeFromXMLFile(filename);
|
||||
}
|
||||
|
||||
public static CustomSettingNames FactoryLoadFromString(string xml)
|
||||
{
|
||||
return XMLHelper<CustomSettingNames>.DeserializeFromXmlString(xml);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace nspector.Common.CustomSettings
|
||||
{
|
||||
[Serializable]
|
||||
public class CustomSettingValue
|
||||
{
|
||||
internal uint SettingValue
|
||||
{
|
||||
get { return Convert.ToUInt32(HexValue.Trim(), 16); }
|
||||
}
|
||||
|
||||
public string UserfriendlyName { get; set; }
|
||||
|
||||
public string HexValue { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using nspector.Common.Helper;
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsDecrypterService : DrsSettingsServiceBase
|
||||
{
|
||||
|
||||
private static readonly byte[] _InternalSettingsKey = new byte[] {
|
||||
0x2f, 0x7c, 0x4f, 0x8b, 0x20, 0x24, 0x52, 0x8d, 0x26, 0x3c, 0x94, 0x77, 0xf3, 0x7c, 0x98, 0xa5,
|
||||
0xfa, 0x71, 0xb6, 0x80, 0xdd, 0x35, 0x84, 0xba, 0xfd, 0xb6, 0xa6, 0x1b, 0x39, 0xc4, 0xcc, 0xb0,
|
||||
0x7e, 0x95, 0xd9, 0xee, 0x18, 0x4b, 0x9c, 0xf5, 0x2d, 0x4e, 0xd0, 0xc1, 0x55, 0x17, 0xdf, 0x18,
|
||||
0x1e, 0x0b, 0x18, 0x8b, 0x88, 0x58, 0x86, 0x5a, 0x1e, 0x03, 0xed, 0x56, 0xfb, 0x16, 0xfe, 0x8a,
|
||||
0x01, 0x32, 0x9c, 0x8d, 0xf2, 0xe8, 0x4a, 0xe6, 0x90, 0x8e, 0x15, 0x68, 0xe8, 0x2d, 0xf4, 0x40,
|
||||
0x37, 0x9a, 0x72, 0xc7, 0x02, 0x0c, 0xd1, 0xd3, 0x58, 0xea, 0x62, 0xd1, 0x98, 0x36, 0x2b, 0xb2,
|
||||
0x16, 0xd5, 0xde, 0x93, 0xf1, 0xba, 0x74, 0xe3, 0x32, 0xc4, 0x9f, 0xf6, 0x12, 0xfe, 0x18, 0xc0,
|
||||
0xbb, 0x35, 0x79, 0x9c, 0x6b, 0x7a, 0x23, 0x7f, 0x2b, 0x15, 0x9b, 0x42, 0x07, 0x1a, 0xff, 0x69,
|
||||
0xfb, 0x9c, 0xbd, 0x23, 0x97, 0xa8, 0x22, 0x63, 0x8f, 0x32, 0xc8, 0xe9, 0x9b, 0x63, 0x1c, 0xee,
|
||||
0x2c, 0xd9, 0xed, 0x8d, 0x3a, 0x35, 0x9c, 0xb1, 0x60, 0xae, 0x5e, 0xf5, 0x97, 0x6b, 0x9f, 0x20,
|
||||
0x8c, 0xf7, 0x98, 0x2c, 0x43, 0x79, 0x95, 0x1d, 0xcd, 0x46, 0x36, 0x6c, 0xd9, 0x67, 0x20, 0xab,
|
||||
0x41, 0x22, 0x21, 0xe5, 0x55, 0x82, 0xf5, 0x27, 0x20, 0xf5, 0x08, 0x07, 0x3f, 0x6d, 0x69, 0xd9,
|
||||
0x1c, 0x4b, 0xf8, 0x26, 0x03, 0x6e, 0xb2, 0x3f, 0x1e, 0xe6, 0xca, 0x3d, 0x61, 0x44, 0xb0, 0x92,
|
||||
0xaf, 0xf0, 0x88, 0xca, 0xe0, 0x5f, 0x5d, 0xf4, 0xdf, 0xc6, 0x4c, 0xa4, 0xe0, 0xca, 0xb0, 0x20,
|
||||
0x5d, 0xc0, 0xfa, 0xdd, 0x9a, 0x34, 0x8f, 0x50, 0x79, 0x5a, 0x5f, 0x7c, 0x19, 0x9e, 0x40, 0x70,
|
||||
0x71, 0xb5, 0x45, 0x19, 0xb8, 0x53, 0xfc, 0xdf, 0x24, 0xbe, 0x22, 0x1c, 0x79, 0xbf, 0x42, 0x89 };
|
||||
|
||||
public DrsDecrypterService(DrsSettingsMetaService metaService) : base(metaService)
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateInternalSettingMap();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private uint GetDwordFromKey(uint offset)
|
||||
{
|
||||
var bytes = new byte[4];
|
||||
bytes[0] = _InternalSettingsKey[(offset + 0) % 256];
|
||||
bytes[1] = _InternalSettingsKey[(offset + 1) % 256];
|
||||
bytes[2] = _InternalSettingsKey[(offset + 2) % 256];
|
||||
bytes[3] = _InternalSettingsKey[(offset + 3) % 256];
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
|
||||
public uint DecryptDwordValue(uint orgValue, uint settingId)
|
||||
{
|
||||
var keyOffset = (settingId << 1);
|
||||
var key = GetDwordFromKey(keyOffset);
|
||||
return orgValue ^ key;
|
||||
}
|
||||
|
||||
public string DecryptStringValue(byte[] rawData, uint settingId)
|
||||
{
|
||||
var keyOffset = (settingId << 1);
|
||||
for (uint i = 0; i < (uint)rawData.Length; i++)
|
||||
{
|
||||
rawData[i] ^= _InternalSettingsKey[(keyOffset + i) % 256];
|
||||
}
|
||||
return Encoding.Unicode.GetString(rawData).Trim('\0');
|
||||
}
|
||||
|
||||
public void DecryptSettingIfNeeded(string profileName, ref NVDRS_SETTING setting)
|
||||
{
|
||||
if (setting.isPredefinedValid == 1)
|
||||
{
|
||||
if (IsInternalSetting(profileName, setting.settingId))
|
||||
{
|
||||
if (setting.settingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE)
|
||||
{
|
||||
setting.predefinedValue.stringValue = DecryptStringValue(setting.predefinedValue.rawData, setting.settingId);
|
||||
if (setting.isCurrentPredefined == 1)
|
||||
setting.currentValue.stringValue = DecryptStringValue(setting.currentValue.rawData, setting.settingId);
|
||||
}
|
||||
else if (setting.settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE)
|
||||
{
|
||||
setting.predefinedValue.dwordValue = DecryptDwordValue(setting.predefinedValue.dwordValue, setting.settingId);
|
||||
if (setting.isCurrentPredefined == 1)
|
||||
setting.currentValue.dwordValue = DecryptDwordValue(setting.currentValue.dwordValue, setting.settingId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatInternalSettingKey(string profileName, uint settingId)
|
||||
{
|
||||
return profileName + settingId.ToString("X8").ToLower();
|
||||
}
|
||||
|
||||
public bool IsInternalSetting(string profileName, uint settingId)
|
||||
{
|
||||
return _InternalSettings.Contains(FormatInternalSettingKey(profileName, settingId));
|
||||
}
|
||||
|
||||
private HashSet<string> _InternalSettings = new HashSet<string>();
|
||||
|
||||
private void CreateInternalSettingMap()
|
||||
{
|
||||
string tmpfile = TempFile.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
SaveSettingsFileEx(hSession, tmpfile);
|
||||
});
|
||||
|
||||
if (File.Exists(tmpfile))
|
||||
{
|
||||
var lines = File.ReadAllLines(tmpfile);
|
||||
|
||||
_InternalSettings = new HashSet<string>();
|
||||
|
||||
var paProfile = "Profile\\s\\\"(?<profileName>.*?)\\\"";
|
||||
var rxProfile = new Regex(paProfile, RegexOptions.Compiled);
|
||||
|
||||
var paSetting = "ID_0x(?<sid>[0-9a-fA-F]+)\\s\\=.*?InternalSettingFlag\\=V0";
|
||||
var rxSetting = new Regex(paSetting, RegexOptions.Compiled);
|
||||
|
||||
var currentProfileName = "";
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
foreach (Match ms in rxProfile.Matches(lines[i]))
|
||||
{
|
||||
currentProfileName = ms.Result("${profileName}");
|
||||
}
|
||||
foreach (Match ms in rxSetting.Matches(lines[i]))
|
||||
{
|
||||
_InternalSettings.Add(currentProfileName + ms.Result("${sid}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tmpfile))
|
||||
File.Delete(tmpfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
using nspector.Common.Helper;
|
||||
using nspector.Common.Import;
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Text;
|
||||
using nvw = nspector.Native.NVAPI2.NvapiDrsWrapper;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsImportService : DrsSettingsServiceBase
|
||||
{
|
||||
|
||||
private readonly DrsSettingsService _SettingService;
|
||||
private readonly DrsScannerService _ScannerService;
|
||||
private readonly DrsDecrypterService _DecrypterService;
|
||||
|
||||
public DrsImportService(
|
||||
DrsSettingsMetaService metaService,
|
||||
DrsSettingsService settingService,
|
||||
DrsScannerService scannerService,
|
||||
DrsDecrypterService decrypterService)
|
||||
: base(metaService)
|
||||
{
|
||||
_SettingService = settingService;
|
||||
_ScannerService = scannerService;
|
||||
_DecrypterService = decrypterService;
|
||||
}
|
||||
|
||||
internal void ExportAllProfilesToNvidiaTextFile(string filename)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
SaveSettingsFileEx(hSession, filename);
|
||||
});
|
||||
}
|
||||
|
||||
internal void ImportAllProfilesFromNvidiaTextFile(string filename)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
LoadSettingsFileEx(hSession, filename);
|
||||
SaveSettings(hSession);
|
||||
}, forceNonGlobalSession: true, preventLoadSettings: true);
|
||||
}
|
||||
|
||||
internal void ExportProfiles(List<string> profileNames, string filename, bool includePredefined)
|
||||
{
|
||||
var exports = new Profiles();
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
foreach (var profileName in profileNames)
|
||||
{
|
||||
var profile = CreateProfileForExport(hSession, profileName, includePredefined);
|
||||
exports.Add(profile);
|
||||
}
|
||||
});
|
||||
|
||||
XMLHelper<Profiles>.SerializeToXmlFile(exports, filename, Encoding.Unicode, true);
|
||||
}
|
||||
|
||||
private Profile CreateProfileForExport(IntPtr hSession, string profileName, bool includePredefined)
|
||||
{
|
||||
var result = new Profile();
|
||||
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
if (hProfile != IntPtr.Zero)
|
||||
{
|
||||
|
||||
result.ProfileName = profileName;
|
||||
|
||||
var apps = GetProfileApplications(hSession, hProfile);
|
||||
foreach (var app in apps)
|
||||
{
|
||||
result.Executeables.Add(app.appName);
|
||||
}
|
||||
|
||||
var settings = GetProfileSettings(hSession, hProfile);
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var isPredefined = setting.isCurrentPredefined == 1;
|
||||
var isCurrentProfile = setting.settingLocation ==
|
||||
NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
|
||||
if (isCurrentProfile && (!isPredefined || includePredefined))
|
||||
{
|
||||
var exportSetting = setting;
|
||||
_DecrypterService.DecryptSettingIfNeeded(profileName, ref exportSetting);
|
||||
|
||||
var profileSetting = ImportExportUitl
|
||||
.ConvertDrsSettingToProfileSetting(exportSetting);
|
||||
|
||||
result.Settings.Add(profileSetting);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal string ImportProfiles(string filename)
|
||||
{
|
||||
var sbFailedProfilesMessage = new StringBuilder();
|
||||
var appInUseHint = false;
|
||||
var profiles = XMLHelper<Profiles>.DeserializeFromXMLFile(filename);
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
foreach (Profile profile in profiles)
|
||||
{
|
||||
var profileCreated = false;
|
||||
var hProfile = GetProfileHandle(hSession, profile.ProfileName);
|
||||
if (hProfile == IntPtr.Zero)
|
||||
{
|
||||
hProfile = CreateProfile(hSession, profile.ProfileName);
|
||||
nvw.DRS_SaveSettings(hSession);
|
||||
profileCreated = true;
|
||||
}
|
||||
|
||||
if (hProfile != IntPtr.Zero)
|
||||
{
|
||||
var modified = false;
|
||||
_SettingService.ResetProfile(profile.ProfileName, out modified);
|
||||
try
|
||||
{
|
||||
UpdateApplications(hSession, hProfile, profile);
|
||||
UpdateSettings(hSession, hProfile, profile, profile.ProfileName);
|
||||
}
|
||||
catch (NvapiException nex)
|
||||
{
|
||||
if (profileCreated)
|
||||
{
|
||||
nvw.DRS_DeleteProfile(hSession, hProfile);
|
||||
}
|
||||
|
||||
sbFailedProfilesMessage.AppendLine(string.Format("Failed to import profile '{0}'", profile.ProfileName));
|
||||
var appEx = nex as NvapiAddApplicationException;
|
||||
if (appEx != null)
|
||||
{
|
||||
var profilesWithThisApp = _ScannerService.FindProfilesUsingApplication(appEx.ApplicationName);
|
||||
sbFailedProfilesMessage.AppendLine(string.Format("- application '{0}' is already in use by profile '{1}'", appEx.ApplicationName, profilesWithThisApp));
|
||||
appInUseHint = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sbFailedProfilesMessage.AppendLine(string.Format("- {0}", nex.Message));
|
||||
}
|
||||
sbFailedProfilesMessage.AppendLine("");
|
||||
}
|
||||
nvw.DRS_SaveSettings(hSession);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (appInUseHint)
|
||||
{
|
||||
sbFailedProfilesMessage.AppendLine("Hint: If just the profile name has been changed by nvidia, consider to manually modify the profile name inside the import file using a text editor.");
|
||||
}
|
||||
|
||||
return sbFailedProfilesMessage.ToString();
|
||||
}
|
||||
|
||||
private bool ExistsImportApp(string appName, Profile importProfile)
|
||||
{
|
||||
return importProfile.Executeables.Any(x => x.Equals(appName));
|
||||
}
|
||||
|
||||
private void UpdateApplications(IntPtr hSession, IntPtr hProfile, Profile importProfile)
|
||||
{
|
||||
var alreadySet = new HashSet<string>();
|
||||
|
||||
var apps = GetProfileApplications(hSession, hProfile);
|
||||
foreach (var app in apps)
|
||||
{
|
||||
if (ExistsImportApp(app.appName, importProfile) && !alreadySet.Contains(app.appName))
|
||||
alreadySet.Add(app.appName);
|
||||
else
|
||||
nvw.DRS_DeleteApplication(hSession, hProfile, new StringBuilder(app.appName));
|
||||
}
|
||||
|
||||
foreach (string appName in importProfile.Executeables)
|
||||
{
|
||||
if (!alreadySet.Contains(appName))
|
||||
{
|
||||
try
|
||||
{
|
||||
AddApplication(hSession, hProfile, appName);
|
||||
}
|
||||
catch (NvapiException)
|
||||
{
|
||||
throw new NvapiAddApplicationException(appName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint GetImportValue(uint settingId, Profile importProfile)
|
||||
{
|
||||
var setting = importProfile.Settings
|
||||
.FirstOrDefault(x => x.SettingId.Equals(settingId));
|
||||
|
||||
if (setting != null)
|
||||
return uint.Parse(setting.SettingValue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private ProfileSetting GetImportProfileSetting(uint settingId, Profile importProfile)
|
||||
{
|
||||
return importProfile.Settings
|
||||
.FirstOrDefault(x => x.SettingId.Equals(settingId));
|
||||
}
|
||||
|
||||
private bool ExistsImportValue(uint settingId, Profile importProfile)
|
||||
{
|
||||
return importProfile.Settings
|
||||
.Any(x => x.SettingId.Equals(settingId));
|
||||
}
|
||||
|
||||
private void UpdateSettings(IntPtr hSession, IntPtr hProfile, Profile importProfile, string profileName)
|
||||
{
|
||||
var alreadySet = new HashSet<uint>();
|
||||
|
||||
var settings = GetProfileSettings(hSession, hProfile);
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var isCurrentProfile = setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
var isPredefined = setting.isCurrentPredefined == 1;
|
||||
|
||||
if (isCurrentProfile)
|
||||
{
|
||||
bool exitsValueInImport = ExistsImportValue(setting.settingId, importProfile);
|
||||
var importSetting = GetImportProfileSetting(setting.settingId, importProfile);
|
||||
|
||||
var decryptedSetting = setting;
|
||||
_DecrypterService.DecryptSettingIfNeeded(profileName, ref decryptedSetting);
|
||||
|
||||
if (isPredefined && exitsValueInImport && ImportExportUitl.AreDrsSettingEqualToProfileSetting(decryptedSetting, importSetting))
|
||||
{
|
||||
alreadySet.Add(setting.settingId);
|
||||
}
|
||||
else if (exitsValueInImport)
|
||||
{
|
||||
var updatedSetting = ImportExportUitl.ConvertProfileSettingToDrsSetting(importSetting);
|
||||
StoreSetting(hSession, hProfile, updatedSetting);
|
||||
alreadySet.Add(setting.settingId);
|
||||
}
|
||||
else if (!isPredefined)
|
||||
{
|
||||
nvw.DRS_DeleteProfileSetting(hSession, hProfile, setting.settingId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var setting in importProfile.Settings)
|
||||
{
|
||||
if (!alreadySet.Contains(setting.SettingId))
|
||||
{
|
||||
var newSetting = ImportExportUitl.ConvertProfileSettingToDrsSetting(setting);
|
||||
try
|
||||
{
|
||||
StoreSetting(hSession, hProfile, newSetting);
|
||||
}
|
||||
catch (NvapiException ex)
|
||||
{
|
||||
if (ex.Status != NvAPI_Status.NVAPI_SETTING_NOT_FOUND)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loaded 100 of 207 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user