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 |
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-13.0.0.0" newVersion="13.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>
|
||||
@@ -1,313 +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>4.4.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>CF0BC153EA664CD97AC6F234B91F45D34630958F</ManifestCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestKeyFile>ColorControl_1_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.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Native.0.1.1\lib\net46\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.10\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NStandard, Version=0.8.9.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NStandard.0.8.9\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.2.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NWin32.1.2.0\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.Net.Http.Formatting, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.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="AppContext.cs" />
|
||||
<Compile Include="CCD.cs" />
|
||||
<Compile Include="LgDevice.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="RemoteControlPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="RemoteControlPanel.Designer.cs">
|
||||
<DependentUpon>RemoteControlPanel.cs</DependentUpon>
|
||||
</Compile>
|
||||
<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="Resources\LG_register.json" />
|
||||
<EmbeddedResource Include="Resources\RC_coords.txt" />
|
||||
<None Include="ColorControl_1_TemporaryKey.pfx" />
|
||||
<EmbeddedResource Include="Resources\LG_remote.jpg" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="RemoteControlForm.resx">
|
||||
<DependentUpon>RemoteControlForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="RemoteControlPanel.resx">
|
||||
<DependentUpon>RemoteControlPanel.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,27 +0,0 @@
|
||||
using NvAPIWrapper.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)
|
||||
{
|
||||
return NvPreset.GenerateColorData(dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Common
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-10
@@ -1,27 +1,47 @@
|
||||
namespace ColorControl
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -32,6 +52,14 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using ColorControl.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Forms
|
||||
{
|
||||
class MessageForms
|
||||
{
|
||||
@@ -13,7 +14,8 @@ namespace ColorControl
|
||||
Text,
|
||||
Numeric,
|
||||
DropDown,
|
||||
Flags
|
||||
Flags,
|
||||
Shortcut,
|
||||
}
|
||||
|
||||
public class FieldDefinition
|
||||
@@ -23,9 +25,23 @@ namespace ColorControl
|
||||
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)
|
||||
@@ -69,7 +85,7 @@ namespace ColorControl
|
||||
{
|
||||
var values = new List<FieldDefinition>();
|
||||
|
||||
var prompt = new Form()
|
||||
var prompt = new MessageForm()
|
||||
{
|
||||
Width = 500,
|
||||
Height = 90 + (fields.Count() * 55),
|
||||
@@ -80,6 +96,7 @@ namespace ColorControl
|
||||
|
||||
var top = 10;
|
||||
var boxes = new List<Control>();
|
||||
var counter = 1;
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
@@ -90,15 +107,15 @@ namespace ColorControl
|
||||
|
||||
Control control;
|
||||
|
||||
switch(field.FieldType)
|
||||
switch (field.FieldType)
|
||||
{
|
||||
case FieldType.Text:
|
||||
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (label.Contains("Ip-address"))
|
||||
{
|
||||
textBox.Mask = "###.###.###.###";
|
||||
textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
//textBox.Mask = "990.990.990.990";
|
||||
//textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
else if (label.Contains("MAC-address"))
|
||||
@@ -110,6 +127,27 @@ namespace ColorControl
|
||||
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 };
|
||||
|
||||
@@ -119,6 +157,11 @@ namespace ColorControl
|
||||
numericEdit.Maximum = field.MaxValue;
|
||||
}
|
||||
|
||||
if (field.Value != null)
|
||||
{
|
||||
numericEdit.Text = field.Value.ToString();
|
||||
}
|
||||
|
||||
control = numericEdit;
|
||||
break;
|
||||
|
||||
@@ -134,7 +177,7 @@ namespace ColorControl
|
||||
if (field.Values != null && field.Values.Any())
|
||||
{
|
||||
comboBox.Items.AddRange(field.Values.ToArray());
|
||||
comboBox.SelectedIndex = 0;
|
||||
comboBox.SelectedIndex = field.Value is string strValue ? comboBox.Items.IndexOf(strValue) : 0;
|
||||
}
|
||||
|
||||
control = comboBox;
|
||||
@@ -145,8 +188,9 @@ namespace ColorControl
|
||||
{
|
||||
Left = 40,
|
||||
Top = top,
|
||||
Width = 200,
|
||||
Height = 100
|
||||
Width = 400,
|
||||
Height = 100,
|
||||
MultiColumn = true
|
||||
};
|
||||
|
||||
top += 80;
|
||||
@@ -154,7 +198,7 @@ namespace ColorControl
|
||||
|
||||
if (field.Values != null && field.Values.Any())
|
||||
{
|
||||
var compoundValue = (int)field.Value;
|
||||
var compoundValue = field.Value is uint ? (int)(uint)field.Value : (int)field.Value;
|
||||
var enumValue = 1;
|
||||
foreach (var value in field.Values)
|
||||
{
|
||||
@@ -177,6 +221,7 @@ namespace ColorControl
|
||||
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 };
|
||||
@@ -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 "{}";
|
||||
}
|
||||
Generated
+1369
-326
File diff suppressed because it is too large.
Load diff
+2029
-372
File diff suppressed because it is too large.
Load diff
+13
-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">
|
||||
@@ -129,6 +69,18 @@
|
||||
<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>
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -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,499 +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;
|
||||
|
||||
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<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 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 Display _currentDisplay;
|
||||
|
||||
public NvService(string dataPath) : base(dataPath, "NvPresets.json")
|
||||
{
|
||||
var converter = new ColorDataConverter();
|
||||
_JsonDeserializer.RegisterConverters(new[] { converter });
|
||||
|
||||
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 (_currentDisplay == null)
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays()[0];
|
||||
}
|
||||
|
||||
return _currentDisplay;
|
||||
}
|
||||
|
||||
public override bool HasDisplaysAttached()
|
||||
{
|
||||
try
|
||||
{
|
||||
return DisplayDevice.GetGDIPrimaryDisplayDevice() != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GlobalSave()
|
||||
{
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
protected override List<NvPreset> GetDefaultPresets()
|
||||
{
|
||||
return 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(string idOrName)
|
||||
{
|
||||
var preset = GetPresetByIdOrName(idOrName);
|
||||
if (preset != null)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ApplyPreset(NvPreset preset, AppContext appContext)
|
||||
{
|
||||
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, useSwitch: appContext.StartUpParams.NoGui);
|
||||
|
||||
applyHdr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (applyHdr)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
|
||||
var colorData = preset.applyColorData ? preset.colorData : display.DisplayDevice.CurrentColorData;
|
||||
|
||||
SetHDRState(display, newHdrEnabled, colorData, appContext.StartUpParams.NoGui);
|
||||
}
|
||||
|
||||
if (preset.applyRefreshRate)
|
||||
{
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.applyDithering)
|
||||
{
|
||||
if (!SetDithering(preset.ditheringEnabled, preset: preset))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
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, 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(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>();
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
NVIDIA.Initialize();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected override void Uninitialize()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
NVIDIA.Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+178
-19
@@ -1,37 +1,78 @@
|
||||
using NLog;
|
||||
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.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;
|
||||
|
||||
DataDir = Utils.GetDataPath();
|
||||
InitLogger();
|
||||
|
||||
LoadConfig();
|
||||
|
||||
if (Config.UseGdiScaling)
|
||||
{
|
||||
Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
|
||||
}
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
AppContext = new AppContext(Config, startUpParams);
|
||||
@@ -50,8 +91,7 @@ namespace ColorControl
|
||||
Utils.CloseConsole();
|
||||
}
|
||||
|
||||
string mutexId = $"Global\\{typeof(MainForm).GUID}";
|
||||
var mutex = new Mutex(true, mutexId, out var mutexCreated);
|
||||
_mutex = new Mutex(true, MutexId, out var mutexCreated);
|
||||
try
|
||||
{
|
||||
if (!mutexCreated)
|
||||
@@ -68,11 +108,22 @@ namespace ColorControl
|
||||
}
|
||||
else
|
||||
{
|
||||
_mutex.WaitOne();
|
||||
try
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
Utils.StartService();
|
||||
}
|
||||
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm(AppContext));
|
||||
|
||||
if (Debugger.IsAttached && !IsRestarting)
|
||||
{
|
||||
Utils.StopService();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -84,17 +135,37 @@ namespace ColorControl
|
||||
{
|
||||
if (mutexCreated)
|
||||
{
|
||||
mutex.Dispose();
|
||||
_mutex?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitLogger()
|
||||
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 logfile = new NLog.Targets.FileTarget("logfile") { FileName = Path.Combine(DataDir, "LogFile.txt") };
|
||||
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
|
||||
@@ -109,24 +180,74 @@ namespace ColorControl
|
||||
{
|
||||
ConfigFilename = Path.Combine(DataDir, "Settings.json");
|
||||
|
||||
if (File.Exists(ConfigFilename))
|
||||
try
|
||||
{
|
||||
var data = File.ReadAllText(ConfigFilename);
|
||||
Config = Utils.JsonSerializer.Deserialize<Config>(data);
|
||||
if (File.Exists(ConfigFilename))
|
||||
{
|
||||
var data = File.ReadAllText(ConfigFilename);
|
||||
Config = JsonConvert.DeserializeObject<Config>(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config = new Config();
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new Config();
|
||||
|
||||
Utils.UseDedicatedElevatedProcess = Config.UseDedicatedElevatedProcess;
|
||||
}
|
||||
|
||||
private static bool HandleStartupParams(StartUpParams startUpParams, Process existingProcess)
|
||||
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;
|
||||
|
||||
@@ -206,6 +327,36 @@ namespace ColorControl
|
||||
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);
|
||||
@@ -215,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("4.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.4.0.0")]
|
||||
+21
-4
@@ -19,7 +19,7 @@ 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", "16.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 {
|
||||
@@ -71,7 +71,7 @@ namespace ColorControl.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Power;120;132
|
||||
/// Looks up a localized string similar to Power;120;136
|
||||
///1;106;228
|
||||
///2;196;228
|
||||
///3;286;228
|
||||
@@ -81,15 +81,32 @@ namespace ColorControl.Properties {
|
||||
///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
|
||||
///Speech;198;633
|
||||
///Channel -;298;622
|
||||
///Home;78;695
|
||||
///Settings;318;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 {
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -32,7 +33,7 @@ namespace ColorControl
|
||||
primaryDisplay = true;
|
||||
}
|
||||
|
||||
public AmdPreset(AmdPreset preset): this()
|
||||
public AmdPreset(AmdPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace ColorControl
|
||||
return presets;
|
||||
}
|
||||
|
||||
public string GetTextForMenuItem()
|
||||
public override string GetTextForMenuItem()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
@@ -1,16 +1,19 @@
|
||||
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<AmdPreset>
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public override string ServiceName => "AMD";
|
||||
|
||||
private ADLDisplayInfo _currentDisplay;
|
||||
|
||||
public AmdService(string dataPath) : base(dataPath, "AmdPresets.json")
|
||||
@@ -47,15 +50,7 @@ namespace ColorControl
|
||||
|
||||
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()
|
||||
@@ -63,7 +58,7 @@ namespace ColorControl
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
public override bool HasDisplaysAttached()
|
||||
public override bool HasDisplaysAttached(bool reinitialize = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -103,7 +98,7 @@ namespace ColorControl
|
||||
var preset = GetPresetByIdOrName(idOrName);
|
||||
if (preset != null)
|
||||
{
|
||||
return ApplyPreset(preset, Program.AppContext);
|
||||
return ApplyPreset(preset);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,7 +106,7 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
public bool ApplyPreset(AmdPreset preset, AppContext appContext)
|
||||
public override bool ApplyPreset(AmdPreset preset)
|
||||
{
|
||||
SetCurrentDisplay(preset);
|
||||
|
||||
@@ -143,9 +138,9 @@ namespace ColorControl
|
||||
|
||||
if (preset.applyRefreshRate)
|
||||
{
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
{
|
||||
result = false;
|
||||
if (!SetRefreshRate(preset.refreshRate))
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,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)
|
||||
@@ -1,4 +1,5 @@
|
||||
using NWin32;
|
||||
using ColorControl.Native;
|
||||
using NWin32;
|
||||
using NWin32.NativeTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -7,15 +8,14 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase
|
||||
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase, new()
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public GraphicsService(string dataPath, string presetsBaseFilename) : base(dataPath, presetsBaseFilename)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~GraphicsService()
|
||||
@@ -23,7 +23,7 @@ namespace ColorControl
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
public abstract bool HasDisplaysAttached();
|
||||
public abstract bool HasDisplaysAttached(bool reinitialize = false);
|
||||
|
||||
protected void ToggleHDR(int delay = 1000)
|
||||
{
|
||||
@@ -52,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;
|
||||
@@ -65,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}");
|
||||
@@ -101,11 +101,35 @@ namespace ColorControl
|
||||
{
|
||||
DEVMODEA devMode;
|
||||
// NativeMethods defines modeNum as an 'uint' but we need to pass '-1'
|
||||
if (Utils.EnumDisplaySettingsA(displayName, Utils.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using ColorControl.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
enum PresetTriggerType
|
||||
{
|
||||
@@ -19,10 +19,14 @@ namespace ColorControl
|
||||
Shutdown = 3,
|
||||
[Description("Standby")]
|
||||
Standby = 4,
|
||||
[Description("Screensaver")]
|
||||
Screensaver = 5,
|
||||
[Description("Reserved5")]
|
||||
Reserved5 = 5,
|
||||
[Description("Process switch")]
|
||||
ProcessSwitch = 6,
|
||||
[Description("Screensaver start")]
|
||||
ScreensaverStart = 7,
|
||||
[Description("Screensaver stop")]
|
||||
ScreensaverStop = 8,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -38,6 +42,10 @@ namespace ColorControl
|
||||
FullScreen = 4,
|
||||
[Description("Notifications Disabled")]
|
||||
NotificationsDisabled = 8,
|
||||
[Description("G-SYNC Disabled")]
|
||||
GsyncDisabled = 16,
|
||||
[Description("G-SYNC Enabled")]
|
||||
GsyncEnabled = 32
|
||||
}
|
||||
|
||||
class PresetTrigger
|
||||
@@ -55,7 +63,9 @@ namespace ColorControl
|
||||
|
||||
public bool TriggerActive(PresetTriggerContext context)
|
||||
{
|
||||
var active = Conditions == PresetConditionType.None || (Conditions.HasFlag(PresetConditionType.SDR) && !context.IsHDRActive) || (Conditions.HasFlag(PresetConditionType.HDR) && context.IsHDRActive);
|
||||
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)
|
||||
@@ -64,12 +74,12 @@ namespace ColorControl
|
||||
|
||||
if (active)
|
||||
{
|
||||
var included = allProcesses || context.ChangedProcesses.Any(cp => IncludedProcesses.Any(ip => cp.ProcessName.Equals(ip, StringComparison.OrdinalIgnoreCase)));
|
||||
var excluded = context.ChangedProcesses.Any(cp => ExcludedProcesses.Any(ep => cp.ProcessName.Equals(ep, StringComparison.OrdinalIgnoreCase)));
|
||||
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.Equals(ip, StringComparison.OrdinalIgnoreCase))));
|
||||
(context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen &&
|
||||
(allProcesses || IncludedProcesses.Any(ip => context.ForegroundProcess.ProcessName.NormEquals(ip))));
|
||||
|
||||
var notificationsDisabledCheck = !Conditions.HasFlag(PresetConditionType.NotificationsDisabled) || context.IsNotificationDisabled;
|
||||
|
||||
@@ -99,6 +109,7 @@ namespace ColorControl
|
||||
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; }
|
||||
@@ -110,6 +121,8 @@ namespace ColorControl
|
||||
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;
|
||||
|
||||
@@ -156,6 +169,11 @@ namespace ColorControl
|
||||
|
||||
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())
|
||||
@@ -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>
|
||||
@@ -1,35 +1,38 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.Common
|
||||
{
|
||||
abstract class ServiceBase<T> where T : PresetBase
|
||||
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 JavaScriptSerializer _JsonSerializer;
|
||||
protected JavaScriptSerializer _JsonDeserializer;
|
||||
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;
|
||||
|
||||
_JsonSerializer = new JavaScriptSerializer();
|
||||
_JsonDeserializer = new JavaScriptSerializer();
|
||||
_jsonConverters = new List<JsonConverter>();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
@@ -55,11 +58,30 @@ namespace ColorControl
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -92,7 +114,7 @@ namespace ColorControl
|
||||
// Hack to convert incorrect triggers
|
||||
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<T>>(json);
|
||||
_presets = JsonConvert.DeserializeObject<List<T>>(json, _jsonConverters.ToArray());
|
||||
|
||||
if (_presets != null)
|
||||
{
|
||||
@@ -134,5 +156,16 @@ namespace ColorControl
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using LgTv;
|
||||
using ColorControl.Common;
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
class LgDevice
|
||||
{
|
||||
@@ -19,12 +19,21 @@ namespace ColorControl
|
||||
public decimal MinValue { get; set; }
|
||||
public decimal MaxValue { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Title { get; set; }
|
||||
public int CurrentValue { get; set; }
|
||||
public int NumberOfValues { get; set; }
|
||||
public LgPreset Preset { get; set; }
|
||||
public bool Advanced { get; set; }
|
||||
public ModelYear FromModelYear { get; set; } = ModelYear.None;
|
||||
public ModelYear ToModelYear { get; set; } = ModelYear.None;
|
||||
}
|
||||
|
||||
public class LgDevicePictureSettings
|
||||
{
|
||||
public int Backlight { get; set; }
|
||||
public int Contrast { get; set; }
|
||||
public int Brightness { get; set; }
|
||||
public int Color { get; set; }
|
||||
}
|
||||
|
||||
public enum PowerState
|
||||
@@ -44,8 +53,20 @@ namespace ColorControl
|
||||
Manually
|
||||
}
|
||||
|
||||
public enum ModelYear
|
||||
{
|
||||
None = 0,
|
||||
SeriesPre2018,
|
||||
Series2018,
|
||||
Series2019,
|
||||
Series2020,
|
||||
Series2021,
|
||||
Series2022
|
||||
}
|
||||
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
public static Func<string, string[], bool> ExternalServiceHandler;
|
||||
public static List<string> DefaultActionsOnGameBar = new() { "backlight", "contrast", "brightness", "color" };
|
||||
|
||||
public string Name { get; private set; }
|
||||
public string IpAddress { get; private set; }
|
||||
@@ -58,7 +79,34 @@ namespace ColorControl
|
||||
public bool PowerOnAfterResume { get; set; }
|
||||
public bool PowerOffOnShutdown { get; set; }
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
public bool PowerOffOnScreenSaver { get; set; }
|
||||
public bool PowerOnAfterScreenSaver { get; set; }
|
||||
public bool PowerOnAfterManualPowerOff { get; set; }
|
||||
public bool PowerOnByWindows { get; set; }
|
||||
public bool PowerOffByWindows { get; set; }
|
||||
public bool TriggersEnabled { get; set; }
|
||||
public int HDMIPortNumber { get; set; }
|
||||
|
||||
private List<string> _actionsForGameBar;
|
||||
|
||||
public List<string> ActionsOnGameBar
|
||||
{
|
||||
get
|
||||
{
|
||||
return _actionsForGameBar;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value?.Any() == false)
|
||||
{
|
||||
_actionsForGameBar = DefaultActionsOnGameBar;
|
||||
}
|
||||
else
|
||||
{
|
||||
_actionsForGameBar = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public PowerState CurrentState { get; set; }
|
||||
@@ -81,51 +129,120 @@ namespace ColorControl
|
||||
|
||||
[JsonIgnore]
|
||||
public string ModelName { get; private set; }
|
||||
[JsonIgnore]
|
||||
public ModelYear Year { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public LgDevicePictureSettings PictureSettings { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string CurrentAppId { get; private set; }
|
||||
|
||||
public event EventHandler PictureSettingsChangedEvent;
|
||||
public event EventHandler PowerStateChangedEvent;
|
||||
|
||||
[JsonConstructor]
|
||||
public LgDevice(string name, string ipAddress, string macAddress, bool isCustom = true, bool isDummy = false)
|
||||
{
|
||||
PictureSettings = new LgDevicePictureSettings();
|
||||
ActionsOnGameBar = new List<string> { "backlight", "contrast", "brightness", "color" };
|
||||
|
||||
Name = name;
|
||||
IpAddress = ipAddress;
|
||||
MacAddress = macAddress;
|
||||
IsCustom = isCustom;
|
||||
IsDummy = isDummy;
|
||||
TriggersEnabled = true;
|
||||
|
||||
AddInvokableAction("WOL", new Func<Dictionary<string, object>, bool>(WakeAction));
|
||||
AddGenericPictureAction("backlight", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("brightness", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("brightness", minValue: 0, maxValue: 100, title: "Brightness/Black Level");
|
||||
AddGenericPictureAction("contrast", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("color", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("pictureMode", typeof(PictureMode));
|
||||
AddGenericPictureAction("colorGamut", typeof(ColorGamut));
|
||||
AddGenericPictureAction("dynamicContrast", typeof(OffToAuto));
|
||||
AddGenericPictureAction("pictureMode", typeof(PictureMode), title: "Picture Mode");
|
||||
AddGenericPictureAction("sharpness", minValue: 0, maxValue: 50);
|
||||
//AddGenericPictureAction("dynamicRange", typeof(DynamicRange), category: "dimensionInfo", title: "Dynamic Range");
|
||||
AddGenericPictureAction("colorGamut", typeof(ColorGamut), title: "Color Gamut");
|
||||
AddGenericPictureAction("dynamicContrast", typeof(OffToHigh), title: "Dynamic Contrast");
|
||||
AddGenericPictureAction("gamma", typeof(GammaExp));
|
||||
AddGenericPictureAction("colorTemperature", minValue: 0, maxValue: 50, title: "Color Temperature");
|
||||
AddGenericPictureAction("whiteBalanceColorTemperature", typeof(WhiteBalanceColorTemperature), title: "White Balance Color Temperature");
|
||||
AddGenericPictureAction("eyeComfortMode", typeof(OffToOn), title: "Eye Comfort Mode");
|
||||
//AddGenericPictureAction("dynamicColor", typeof(OffToAuto));
|
||||
//AddGenericPictureAction("superResolution", typeof(OffToAuto));
|
||||
AddGenericPictureAction("peakBrightness", typeof(OffToAuto));
|
||||
AddGenericPictureAction("smoothGradation", typeof(OffToAuto));
|
||||
AddGenericPictureAction("energySaving", typeof(EnergySaving));
|
||||
AddGenericPictureAction("hdrDynamicToneMapping", typeof(DynamicTonemapping));
|
||||
//AddGenericPictureAction("blackLevel", typeof(LowToAuto));
|
||||
AddGenericPictureAction("peakBrightness", typeof(OffToHigh), title: "Peak Brightness", fromModelYear: ModelYear.Series2019);
|
||||
AddGenericPictureAction("smoothGradation", typeof(OffToAuto), title: "Smooth Gradation", fromModelYear: ModelYear.Series2019);
|
||||
AddGenericPictureAction("energySaving", typeof(EnergySaving), title: "Energy Saving");
|
||||
AddGenericPictureAction("hdrDynamicToneMapping", typeof(DynamicTonemapping), title: "HDR Dynamic Tone Mapping");
|
||||
AddGenericPictureAction("blackLevel", typeof(BlackLevel), title: "HDMI Black Level");
|
||||
AddGenericPictureAction("arcPerApp", typeof(AspectRatio), title: "Aspect Ratio", category: "aspectRatio");
|
||||
AddGenericPictureAction("justScan", typeof(OffToAuto2), title: "Just Scan", category: "aspectRatio");
|
||||
AddGenericPictureAction("allDirZoomHRatio", minValue: 0, maxValue: 10, title: "All-Direction Zoom Horizontal Ratio", category: "aspectRatio");
|
||||
AddGenericPictureAction("allDirZoomVRatio", minValue: 0, maxValue: 9, title: "All-Direction Zoom Vertical Ratio", category: "aspectRatio");
|
||||
AddGenericPictureAction("allDirZoomHPosition", minValue: -10, maxValue: 9, title: "All-Direction Zoom Horizontal Position", category: "aspectRatio");
|
||||
AddGenericPictureAction("allDirZoomVPosition", minValue: -9, maxValue: 9, title: "All-Direction Zoom Vertical Position", category: "aspectRatio");
|
||||
AddGenericPictureAction("vertZoomVPosition", minValue: -8, maxValue: 9, title: "Vertical Zoom Position", category: "aspectRatio");
|
||||
AddGenericPictureAction("vertZoomVRatio", minValue: 0, maxValue: 9, title: "Vertical Zoom Ratio", category: "aspectRatio");
|
||||
//AddGenericPictureAction("ambientLightCompensation", typeof(OffToAuto2));
|
||||
//AddGenericPictureAction("truMotionMode", typeof(TruMotionMode));
|
||||
AddGenericPictureAction("motionProOLED", typeof(OffToHigh));
|
||||
AddGenericPictureAction("truMotionMode", typeof(TruMotionMode), title: "TruMotion");
|
||||
AddGenericPictureAction("truMotionJudder", minValue: 0, maxValue: 10, title: "TruMotion Judder");
|
||||
AddGenericPictureAction("truMotionBlur", minValue: 0, maxValue: 10, title: "TruMotion Blur");
|
||||
AddGenericPictureAction("motionProOLED", typeof(OffToHigh), title: "OLED Motion Pro", fromModelYear: ModelYear.Series2019);
|
||||
AddGenericPictureAction("motionPro", typeof(OffToOn), title: "Motion Pro");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI1", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI2", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI3", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI4", typeof(OffToOn), category: "other");
|
||||
//AddGenericPictureAction("hdmiPcMode", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("lowLevelAdjustment", minValue: -30, maxValue: 30, category: "other", title: "Fine Tune Dark Areas", fromModelYear: ModelYear.Series2019);
|
||||
AddGenericPictureAction("blackStabilizer", minValue: -30, maxValue: 30, category: "other", title: "Black Stabilizer", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("whiteStabilizer", minValue: -30, maxValue: 30, category: "other", title: "White Stabilizer", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("blueLight", typeof(BlueLight), category: "other", title: "Reduce Blue Light", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameMode_hdmi1", typeof(OffToOn), category: "other", title: "Game Optimizer HDMI1", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameMode_hdmi2", typeof(OffToOn), category: "other", title: "Game Optimizer HDMI2", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameMode_hdmi3", typeof(OffToOn), category: "other", title: "Game Optimizer HDMI3", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameMode_hdmi4", typeof(OffToOn), category: "other", title: "Game Optimizer HDMI4", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameOptimization", typeof(OffToOn), category: "other", title: "VRR & G-Sync", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("inputOptimization", typeof(InputOptimization), category: "other", title: "Prevent Input Delay(Input Lag)", fromModelYear: ModelYear.Series2021);
|
||||
AddGenericPictureAction("gameOptimizationHDMI1", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI2", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI3", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI4", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("adjustingLuminance", minValue: -50, maxValue: 50);
|
||||
AddGenericPictureAction("freesync", typeof(OffToOn), category: "other", title: "AMD FreeSync Premium", fromModelYear: ModelYear.Series2020);
|
||||
AddGenericPictureAction("freesyncOLEDHDMI1", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020);
|
||||
AddGenericPictureAction("freesyncOLEDHDMI2", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020);
|
||||
AddGenericPictureAction("freesyncOLEDHDMI3", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020);
|
||||
AddGenericPictureAction("freesyncOLEDHDMI4", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020);
|
||||
//AddGenericPictureAction("freesyncSupport", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi1", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi2", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi3", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi4", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("adjustingLuminance", minValue: -50, maxValue: 50, numberOfValues: 22);
|
||||
AddGenericPictureAction("whiteBalanceBlue", minValue: -50, maxValue: 50, numberOfValues: 22);
|
||||
AddGenericPictureAction("whiteBalanceGreen", minValue: -50, maxValue: 50, numberOfValues: 22);
|
||||
AddGenericPictureAction("whiteBalanceRed", minValue: -50, maxValue: 50, numberOfValues: 22);
|
||||
//AddGenericPictureAction("wb20PointsGammaValue", minValue: -50, maxValue: 50);
|
||||
AddInvokableAction("turnScreenOff", new Func<Dictionary<string, object>, bool>(TurnScreenOffAction));
|
||||
AddInvokableAction("turnScreenOn", new Func<Dictionary<string, object>, bool>(TurnScreenOnAction));
|
||||
|
||||
AddInternalPresetAction(new LgPreset("InStart", "com.webos.app.factorywin", new[] { "0", "4", "1", "3" }, new { id = "executeFactory", irKey = "inStart" }));
|
||||
AddInternalPresetAction(new LgPreset("EzAdjust", "com.webos.app.factorywin", new[] { "0", "4", "1", "3" }, new { id = "executeFactory", irKey = "ezAdjust" }));
|
||||
//AddInternalPresetAction(new LgPreset("PictureCheck", "com.webos.app.factorywin", null, new { id = "executeFactory", irKey = "pCheck" }));
|
||||
AddInternalPresetAction(new LgPreset("Software Update", "com.webos.app.softwareupdate", null, new { mode = "user", flagUpdate = true }));
|
||||
|
||||
AddSetDeviceConfigAction("HDMI_1_icon", typeof(HdmiIcon), "HDMI 1 icon");
|
||||
AddSetDeviceConfigAction("HDMI_2_icon", typeof(HdmiIcon), "HDMI 2 icon");
|
||||
AddSetDeviceConfigAction("HDMI_3_icon", typeof(HdmiIcon), "HDMI 3 icon");
|
||||
AddSetDeviceConfigAction("HDMI_4_icon", typeof(HdmiIcon), "HDMI 4 icon");
|
||||
|
||||
AddGenericPictureAction("soundMode", typeof(SoundMode), category: "sound", title: "Sound Mode");
|
||||
AddGenericPictureAction("soundOutput", typeof(SoundOutput), category: "sound", title: "Sound Output");
|
||||
AddGenericPictureAction("autoVolume", typeof(OffToOn), category: "sound", title: "Auto Volume");
|
||||
|
||||
//AddGenericPictureAction("enableToastPopup", typeof(OffToOn), category: "option", title: "enableToastPopup");
|
||||
AddSetConfigAction("tv.conti.supportUsedTime", typeof(BoolFalseToTrue), title: "Total Power On Time");
|
||||
|
||||
AddGenericPictureAction("wolwowlOnOff", typeof(FalseToTrue), category: "network", title: "Wake-On-LAN");
|
||||
}
|
||||
|
||||
private void AddInvokableAction(string name, Func<Dictionary<string, object>, bool> function)
|
||||
@@ -139,7 +256,18 @@ namespace ColorControl
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
private void AddGenericPictureAction(string name, Type type = null, decimal minValue = 0, decimal maxValue = 0, string category = "picture")
|
||||
private void AddInternalPresetAction(LgPreset preset)
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
Name = preset.name,
|
||||
Preset = preset
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
private void AddGenericPictureAction(string name, Type type = null, decimal minValue = 0, decimal maxValue = 0, string category = "picture", string title = null, int numberOfValues = 1, ModelYear fromModelYear = ModelYear.None)
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
@@ -148,12 +276,56 @@ namespace ColorControl
|
||||
EnumType = type,
|
||||
MinValue = minValue,
|
||||
MaxValue = maxValue,
|
||||
Category = category
|
||||
NumberOfValues = numberOfValues,
|
||||
Category = category,
|
||||
Title = title == null ? Utils.FirstCharUpperCase(name) : title,
|
||||
FromModelYear = fromModelYear
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
private void AddSetDeviceConfigAction(string name, Type type, string title)
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
Name = name,
|
||||
Function = new Func<Dictionary<string, object>, bool>(GenericDeviceConfigAction),
|
||||
EnumType = type,
|
||||
Title = title == null ? Utils.FirstCharUpperCase(name) : title
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
private void AddSetConfigAction(string name, Type type, string title)
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
Name = name,
|
||||
Function = new Func<Dictionary<string, object>, bool>(GenericSetConfigAction),
|
||||
EnumType = type,
|
||||
Title = title == null ? Utils.FirstCharUpperCase(name) : title,
|
||||
Category = "Config",
|
||||
Advanced = true
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
public void AddGameBarAction(string name)
|
||||
{
|
||||
if (!ActionsOnGameBar.Contains(name))
|
||||
{
|
||||
ActionsOnGameBar.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveGameBarAction(string name)
|
||||
{
|
||||
ActionsOnGameBar.Remove(name);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
//return (IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: ")) + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $" ({IpAddress})" : string.Empty);
|
||||
@@ -178,17 +350,23 @@ namespace ColorControl
|
||||
|
||||
//Test();
|
||||
//_lgTvApi.Test3();
|
||||
if (_lgTvApi != null)
|
||||
if (_lgTvApi != null && !Utils.ConsoleOpened)
|
||||
{
|
||||
var info = await _lgTvApi.GetSystemInfo("modelName");
|
||||
if (info != null)
|
||||
{
|
||||
ModelName = info.modelName;
|
||||
SetModelYear();
|
||||
}
|
||||
|
||||
//await _lgTvApi.SubscribeVolume(VolumeChanged);
|
||||
await _lgTvApi.SubscribePowerState(PowerStateChanged);
|
||||
await _lgTvApi.SubscribePictureSettings(PictureSettingsChanged);
|
||||
await _lgTvApi.SubscribeForegroundApp(ForegroundAppChanged);
|
||||
|
||||
//await _lgTvApi.SetConfig("tv.conti.supportUsedTime", true);
|
||||
|
||||
//await _lgTvApi.Reboot();
|
||||
|
||||
//var result = await GetPictureSettings();
|
||||
|
||||
@@ -215,6 +393,38 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
private void SetModelYear()
|
||||
{
|
||||
if (ModelName == null || ModelName.Contains("OLED") != true)
|
||||
{
|
||||
Year = ModelYear.None;
|
||||
return;
|
||||
}
|
||||
|
||||
var normalized = ModelName.Replace("OLED", "").Replace(" ", "");
|
||||
|
||||
if (normalized.Length > 3)
|
||||
{
|
||||
normalized = normalized.Substring(3);
|
||||
}
|
||||
|
||||
var suffix = normalized.First();
|
||||
|
||||
Year = suffix switch
|
||||
{
|
||||
'6' => ModelYear.SeriesPre2018,
|
||||
'7' => ModelYear.SeriesPre2018,
|
||||
'8' => ModelYear.Series2018,
|
||||
'9' => ModelYear.Series2019,
|
||||
'X' => ModelYear.Series2020,
|
||||
'1' => ModelYear.Series2021,
|
||||
'2' => ModelYear.Series2022,
|
||||
_ => ModelYear.None
|
||||
};
|
||||
|
||||
_invokableActions = _invokableActions.Where(a => Year == ModelYear.None || a.FromModelYear == ModelYear.None || Year >= a.FromModelYear).ToList();
|
||||
}
|
||||
|
||||
public bool VolumeChanged(dynamic payload)
|
||||
{
|
||||
return true;
|
||||
@@ -226,7 +436,7 @@ namespace ColorControl
|
||||
{
|
||||
var settings = payload.settings;
|
||||
|
||||
Logger.Debug($"PictureSettingsChanged: {JsonConvert.SerializeObject(settings)}");
|
||||
Logger.Debug($"[{Name}] PictureSettingsChanged: {JsonConvert.SerializeObject(settings)}");
|
||||
|
||||
if (settings.backlight != null)
|
||||
{
|
||||
@@ -236,8 +446,18 @@ namespace ColorControl
|
||||
{
|
||||
PictureSettings.Contrast = Utils.ParseDynamicAsInt(settings.contrast, PictureSettings.Contrast);
|
||||
}
|
||||
if (settings.brightness != null)
|
||||
{
|
||||
PictureSettings.Brightness = Utils.ParseDynamicAsInt(settings.brightness, PictureSettings.Brightness);
|
||||
}
|
||||
if (settings.color != null)
|
||||
{
|
||||
PictureSettings.Color = Utils.ParseDynamicAsInt(settings.color, PictureSettings.Color);
|
||||
}
|
||||
}
|
||||
|
||||
PictureSettingsChangedEvent?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -245,7 +465,7 @@ namespace ColorControl
|
||||
{
|
||||
Logger.Debug($"[{Name}] Power state change: {JsonConvert.SerializeObject(payload)}");
|
||||
|
||||
var state = ((string)payload.state).Replace(' ', '_');
|
||||
var state = payload.state != null ? ((string)payload.state).Replace(' ', '_') : PowerState.Unknown.ToString();
|
||||
|
||||
PowerState newState;
|
||||
if (Enum.TryParse(state, out newState))
|
||||
@@ -260,8 +480,13 @@ namespace ColorControl
|
||||
PoweredOffBy = PowerOffSource.Unknown;
|
||||
_poweredOffViaAppDateTime = DateTimeOffset.MinValue;
|
||||
}
|
||||
else if (payload.processing != null && ((string)payload.processing).Equals("Request Power Off", StringComparison.Ordinal))
|
||||
{
|
||||
PoweredOffBy = PoweredOffViaApp ? PowerOffSource.App : PowerOffSource.Manually;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
PoweredOffBy = PoweredOffViaApp ? PowerOffSource.App : PowerOffSource.Manually;
|
||||
}
|
||||
}
|
||||
@@ -273,6 +498,20 @@ namespace ColorControl
|
||||
|
||||
Logger.Debug($"PoweredOffBy: {PoweredOffBy}, PoweredOffViaApp: {PoweredOffViaApp}");
|
||||
|
||||
PowerStateChangedEvent?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ForegroundAppChanged(dynamic payload)
|
||||
{
|
||||
Logger.Debug($"[{Name}] ForegroundAppChanged: {JsonConvert.SerializeObject(payload)}");
|
||||
|
||||
if (payload.appId != null)
|
||||
{
|
||||
CurrentAppId = payload.appId;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -316,11 +555,26 @@ namespace ColorControl
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic @params = null;
|
||||
if (config.AllowFirmwareDowngrade && preset.appId.Equals("com.webos.app.softwareupdate"))
|
||||
var @params = preset.AppParams;
|
||||
if (@params == null && config.ShowAdvancedActions)
|
||||
{
|
||||
@params = new { mode = "user", flagUpdate = true };
|
||||
if (preset.appId.Equals("com.webos.app.softwareupdate"))
|
||||
{
|
||||
@params = new { mode = "user", flagUpdate = true };
|
||||
}
|
||||
else if (preset.appId.Equals("com.webos.app.factorywin"))
|
||||
{
|
||||
if (preset.name.Contains("ezadjust", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@params = new { id = "executeFactory", irKey = "ezAdjust" };
|
||||
}
|
||||
else
|
||||
{
|
||||
@params = new { id = "executeFactory", irKey = "inStart" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _lgTvApi.LaunchApp(preset.appId, @params);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -373,34 +627,37 @@ namespace ColorControl
|
||||
|
||||
private async Task ExecuteSteps(LgTvApi api, LgPreset preset)
|
||||
{
|
||||
var mouse = await api.GetMouse();
|
||||
LgWebOsMouseService mouse = null;
|
||||
|
||||
foreach (var step in preset.steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
var key = keySpec[0].ToUpper();
|
||||
|
||||
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 = keySpec[0].Split('(');
|
||||
var keyValue = key.Split('(');
|
||||
key = keyValue[0];
|
||||
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
|
||||
}
|
||||
|
||||
var delay = 0;
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
delay = Utils.ParseInt(keySpec[1]);
|
||||
}
|
||||
|
||||
var executeKey = true;
|
||||
var action = _invokableActions.FirstOrDefault(a => a.Name.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
if (action != null)
|
||||
{
|
||||
if (!key.Equals("WOL"))
|
||||
{
|
||||
ExecuteAction(action, parameters);
|
||||
}
|
||||
ExecuteAction(action, parameters);
|
||||
|
||||
executeKey = false;
|
||||
}
|
||||
@@ -414,6 +671,7 @@ namespace ColorControl
|
||||
|
||||
if (executeKey)
|
||||
{
|
||||
mouse ??= await api.GetMouse();
|
||||
SendKey(mouse, key);
|
||||
delay = delay == 0 ? 180 : delay;
|
||||
}
|
||||
@@ -425,9 +683,13 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteAction(InvokableAction action, string[] parameters)
|
||||
public void ExecuteAction(InvokableAction action, string[] parameters)
|
||||
{
|
||||
var function = action.Function;
|
||||
if (function == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
@@ -446,7 +708,8 @@ namespace ColorControl
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key)
|
||||
{
|
||||
if (key.Length == 1 && int.TryParse(key, out _))
|
||||
key = key.ToUpper();
|
||||
if (key.Length >= 1 && int.TryParse(key[0].ToString(), out _))
|
||||
{
|
||||
key = "_" + key;
|
||||
}
|
||||
@@ -461,6 +724,11 @@ namespace ColorControl
|
||||
|
||||
public async Task<IEnumerable<LgApp>> GetApps(bool force = false)
|
||||
{
|
||||
if (!force)
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
|
||||
if (!await Connected(force))
|
||||
{
|
||||
Logger.Debug("Cannot refresh apps: no connection could be made");
|
||||
@@ -470,17 +738,30 @@ namespace ColorControl
|
||||
return await _lgTvApi.GetApps(force);
|
||||
}
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
internal async Task<bool> PowerOff(bool checkHdmi = false)
|
||||
{
|
||||
if (!await Connected(true) || CurrentState != PowerState.Active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkHdmi && CurrentAppId != null && HDMIPortNumber != 0 && !CurrentAppId.EndsWith($".hdmi{HDMIPortNumber}", StringComparison.InvariantCulture))
|
||||
{
|
||||
Logger.Debug($"[{Name}] PowerOff is ignored because current app {CurrentAppId} does not match configured HDMI port {HDMIPortNumber}");
|
||||
return true;
|
||||
}
|
||||
|
||||
PoweredOffViaApp = true;
|
||||
_poweredOffViaAppDateTime = DateTimeOffset.Now;
|
||||
|
||||
await _lgTvApi.TurnOff();
|
||||
try
|
||||
{
|
||||
await _lgTvApi.TurnOff().WaitAsync(TimeSpan.FromSeconds(2));
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
Logger.Debug($"Timeout when turning off tv: {ex.Message}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -526,7 +807,7 @@ namespace ColorControl
|
||||
}
|
||||
Logger.Debug("WOL succeeded");
|
||||
await Task.Delay(connectDelay);
|
||||
result = await Connect();
|
||||
result = await Connect(1);
|
||||
Logger.Debug("Connect succeeded: " + result);
|
||||
return result;
|
||||
}
|
||||
@@ -543,7 +824,7 @@ namespace ColorControl
|
||||
|
||||
if (MacAddress != null)
|
||||
{
|
||||
result = WOL.WakeFunction(MacAddress);
|
||||
result = WOL.WakeFunctionCheckAdmin(MacAddress, IpAddress);
|
||||
_justWokeUp = true;
|
||||
}
|
||||
else
|
||||
@@ -577,21 +858,24 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
var resumeScript = Path.Combine(Program.DataDir, "ResumeScript.bat");
|
||||
if (File.Exists(resumeScript))
|
||||
{
|
||||
Utils.StartProcess(resumeScript);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<InvokableAction> GetInvokableActions()
|
||||
public List<InvokableAction> GetInvokableActions(bool includedAdvanced = false)
|
||||
{
|
||||
return _invokableActions;
|
||||
return _invokableActions.Where(a => includedAdvanced || !a.Advanced).ToList();
|
||||
}
|
||||
|
||||
public List<InvokableAction> GetInvokableActionsForGameBar()
|
||||
{
|
||||
var actions = GetInvokableActions();
|
||||
return actions.Where(a => a.Category == "picture" && (a.EnumType != null && a.EnumType != typeof(PictureMode) || a.MinValue >= 0 && a.MaxValue > 0)).ToList();
|
||||
}
|
||||
|
||||
public List<InvokableAction> GetActionsForGameBar()
|
||||
{
|
||||
var actions = GetInvokableActions();
|
||||
return actions.Where(a => ActionsOnGameBar.Contains(a.Name)).ToList();
|
||||
}
|
||||
|
||||
public async Task PowerOn()
|
||||
@@ -642,26 +926,33 @@ namespace ColorControl
|
||||
await _lgTvApi.SetSystemSettings("backlight", backlight.ToString());
|
||||
}
|
||||
|
||||
internal async Task SetContrast(int contrast)
|
||||
{
|
||||
await _lgTvApi.SetSystemSettings("contrast", contrast.ToString());
|
||||
}
|
||||
|
||||
public async Task SetOLEDMotionPro(string mode)
|
||||
{
|
||||
await _lgTvApi.SetConfig("tv.model.motionProMode", mode);
|
||||
}
|
||||
|
||||
internal async Task SetConfig(string key, string value)
|
||||
internal async Task SetConfig(string key, object value)
|
||||
{
|
||||
await _lgTvApi.SetConfig(key, value);
|
||||
}
|
||||
internal async Task SetSystemSettings(string name, string value)
|
||||
{
|
||||
await _lgTvApi.SetSystemSettings(name, value);
|
||||
|
||||
UpdateCurrentValueOfAction(name, value);
|
||||
}
|
||||
|
||||
internal async Task<dynamic> GetPictureSettings()
|
||||
{
|
||||
//var keys = new[] { "backlight", "brightness", "contrast", "color", "pictureMode", "colorGamut", "dynamicContrast", "peakBrightness", "smoothGradation", "energySaving", "motionProOLED" };
|
||||
var keys = new[] { "backlight", "brightness", "contrast", "color" };
|
||||
//var keys = new[] { "backlight", "brightness", "contrast", "color" };
|
||||
|
||||
return await _lgTvApi.GetSystemSettings("picture", keys);
|
||||
return await _lgTvApi.GetSystemSettings2("picture");
|
||||
}
|
||||
|
||||
private bool WakeAction(Dictionary<string, object> parameters)
|
||||
@@ -698,7 +989,60 @@ namespace ColorControl
|
||||
var task = _lgTvApi.SetSystemSettings(settingName, value, category);
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
UpdateCurrentValueOfAction(settingName, value.ToString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool GenericDeviceConfigAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
var id = parameters["name"].ToString().Replace("_icon", string.Empty);
|
||||
var stringValues = parameters["value"] as string[];
|
||||
var value = stringValues[0];
|
||||
|
||||
var description = Utils.GetDescriptionByEnumName<HdmiIcon>(value);
|
||||
|
||||
var task = _lgTvApi.SetDeviceConfig(id, value, description);
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool GenericSetConfigAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
var key = parameters["name"].ToString();
|
||||
var values = parameters["value"] as object[];
|
||||
var value = values[0];
|
||||
|
||||
var task = _lgTvApi.SetConfig(key, value);
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateCurrentValueOfAction(string settingName, string value)
|
||||
{
|
||||
if (settingName != "backlight" && settingName != "contrast" && settingName != "brightness" && settingName != "color")
|
||||
{
|
||||
var action = _invokableActions.FirstOrDefault(a => a.Name == settingName);
|
||||
if (action != null)
|
||||
{
|
||||
if (action.EnumType != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumValue = Enum.Parse(action.EnumType, value);
|
||||
var intEnum = (int)enumValue;
|
||||
action.CurrentValue = intEnum;
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
else
|
||||
{
|
||||
action.CurrentValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+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>
|
||||
@@ -1,19 +1,19 @@
|
||||
using LgTv;
|
||||
using ColorControl.Services.Common;
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColorControl
|
||||
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; }
|
||||
@@ -37,6 +37,17 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1,4 +1,8 @@
|
||||
using LgTv;
|
||||
using ColorControl.Common;
|
||||
using ColorControl.Forms;
|
||||
using ColorControl.Services.Common;
|
||||
using ColorControl.Svc;
|
||||
using LgTv;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using NWin32;
|
||||
@@ -7,10 +11,11 @@ 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
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
class LgService : ServiceBase<LgPreset>
|
||||
{
|
||||
@@ -23,6 +28,13 @@ namespace ColorControl
|
||||
StandBy = 5
|
||||
}
|
||||
|
||||
internal enum WindowsPowerSetting
|
||||
{
|
||||
Off = 0,
|
||||
On = 1,
|
||||
Dimmed = 2
|
||||
}
|
||||
|
||||
internal class ProcessMonitorContext
|
||||
{
|
||||
public List<LgDevice> Devices { get; set; }
|
||||
@@ -40,6 +52,8 @@ namespace ColorControl
|
||||
|
||||
public static string LgListAppsJson = "listApps.json";
|
||||
|
||||
public override string ServiceName => "LG";
|
||||
|
||||
public List<LgDevice> Devices { get; private set; }
|
||||
public LgDevice SelectedDevice
|
||||
{
|
||||
@@ -48,10 +62,14 @@ namespace ColorControl
|
||||
{
|
||||
_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;
|
||||
@@ -64,13 +82,17 @@ namespace ColorControl
|
||||
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();
|
||||
@@ -109,24 +131,10 @@ namespace ColorControl
|
||||
SaveConfig();
|
||||
SavePresets();
|
||||
SaveRemoteControlButtons();
|
||||
|
||||
SendConfigToService();
|
||||
}
|
||||
|
||||
public LgPreset CreateNewPreset()
|
||||
{
|
||||
var preset = new LgPreset();
|
||||
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 override List<LgPreset> GetDefaultPresets()
|
||||
{
|
||||
List<LgPreset> presets = null;
|
||||
@@ -136,7 +144,7 @@ namespace ColorControl
|
||||
{
|
||||
var json = File.ReadAllText(defaultPresetsFileName);
|
||||
|
||||
presets = _JsonDeserializer.Deserialize<List<LgPreset>>(json);
|
||||
presets = JsonConvert.DeserializeObject<List<LgPreset>>(json);
|
||||
}
|
||||
|
||||
return presets;
|
||||
@@ -145,14 +153,18 @@ namespace ColorControl
|
||||
private void LoadConfig()
|
||||
{
|
||||
_configFilename = Path.Combine(_dataPath, "LgConfig.json");
|
||||
if (File.Exists(_configFilename))
|
||||
try
|
||||
{
|
||||
Config = JsonConvert.DeserializeObject<LgServiceConfig>(File.ReadAllText(_configFilename));
|
||||
if (File.Exists(_configFilename))
|
||||
{
|
||||
Config = JsonConvert.DeserializeObject<LgServiceConfig>(File.ReadAllText(_configFilename));
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config = new LgServiceConfig();
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new LgServiceConfig();
|
||||
|
||||
LgPreset.LgDevices = Config.Devices;
|
||||
}
|
||||
@@ -193,6 +205,7 @@ namespace ColorControl
|
||||
}
|
||||
|
||||
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"));
|
||||
@@ -250,29 +263,19 @@ namespace ColorControl
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = _JsonSerializer.Serialize(_presets);
|
||||
File.WriteAllText(_presetsFilename, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e.ToLogString());
|
||||
}
|
||||
Utils.WriteObject(_presetsFilename, _presets);
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
Config.PowerOnAfterStartup = Devices?.Any(d => d.PowerOnAfterStartup) ?? false;
|
||||
|
||||
var json = JsonConvert.SerializeObject(Config);
|
||||
File.WriteAllText(_configFilename, json);
|
||||
Utils.WriteObject(_configFilename, Config);
|
||||
}
|
||||
|
||||
private void SaveRemoteControlButtons()
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(_remoteControlButtons);
|
||||
File.WriteAllText(_rcButtonsFilename, json);
|
||||
Utils.WriteObject(_rcButtonsFilename, _remoteControlButtons);
|
||||
}
|
||||
|
||||
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
|
||||
@@ -287,7 +290,7 @@ namespace ColorControl
|
||||
|
||||
Devices.RemoveAll(d => !d.IsCustom && !autoIpAddresses.Contains(d.IpAddress));
|
||||
|
||||
var newAutoDevices = autoDevices.Where(ad => !Devices.Any(d => d.IpAddress.Equals(ad.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())
|
||||
@@ -314,6 +317,11 @@ namespace ColorControl
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var device in Devices)
|
||||
{
|
||||
device.PowerStateChangedEvent += LgDevice_PowerStateChangedEvent;
|
||||
}
|
||||
|
||||
if (connect && SelectedDevice != null)
|
||||
{
|
||||
if (_allowPowerOn)
|
||||
@@ -340,6 +348,15 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -397,22 +414,6 @@ namespace ColorControl
|
||||
return await SelectedDevice?.PowerOff();
|
||||
}
|
||||
|
||||
internal Task PowerOffOnShutdownOrResume(PowerOnOffState state = PowerOnOffState.ShutDown)
|
||||
{
|
||||
var devices = Devices.Where(d => state == PowerOnOffState.ShutDown && d.PowerOffOnShutdown ||
|
||||
state == PowerOnOffState.StandBy && d.PowerOffOnStandby);
|
||||
|
||||
var tasks = new List<Task>();
|
||||
foreach (var device in devices)
|
||||
{
|
||||
var task = device.PowerOff();
|
||||
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
return Task.WhenAll(tasks.ToArray());
|
||||
}
|
||||
|
||||
internal bool WakeSelectedDevice()
|
||||
{
|
||||
return SelectedDevice?.Wake() ?? false;
|
||||
@@ -420,26 +421,11 @@ namespace ColorControl
|
||||
|
||||
internal void WakeAfterStartupOrResume(PowerOnOffState state = PowerOnOffState.StartUp, bool checkUserSession = true)
|
||||
{
|
||||
if (checkUserSession && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
Logger.Debug($"WakeAfterStartupOrResume: not waking because session info indicates no local session");
|
||||
return;
|
||||
}
|
||||
var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup ||
|
||||
state == PowerOnOffState.Resume && d.PowerOnAfterResume ||
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerOnAfterScreenSaver);
|
||||
|
||||
var wakeDevices = Devices.Where(d => state == PowerOnOffState.StartUp && d.PowerOnAfterStartup ||
|
||||
state == PowerOnOffState.Resume && d.PowerOnAfterResume ||
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver);
|
||||
foreach (var device in wakeDevices)
|
||||
{
|
||||
if (device.PoweredOffBy == LgDevice.PowerOffSource.Manually)
|
||||
{
|
||||
Logger.Debug($"[{device.Name}]: device was manually powered off by user, not powering on");
|
||||
continue;
|
||||
}
|
||||
|
||||
device.DisposeConnection();
|
||||
var _ = device.WakeAndConnectWithRetries(Config.PowerOnRetries);
|
||||
}
|
||||
PowerOnDevices(wakeDevices, state, checkUserSession);
|
||||
}
|
||||
|
||||
public void InstallEventHandlers()
|
||||
@@ -485,27 +471,8 @@ namespace ColorControl
|
||||
return;
|
||||
}
|
||||
|
||||
if (Devices.Any(d => d.PowerOffOnStandby))
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS | NativeConstants.ES_SYSTEM_REQUIRED | NativeConstants.ES_AWAYMODE_REQUIRED);
|
||||
try
|
||||
{
|
||||
var standByScript = Path.Combine(Program.DataDir, "StandByScript.bat");
|
||||
if (File.Exists(standByScript))
|
||||
{
|
||||
Utils.StartProcess(standByScript);
|
||||
}
|
||||
|
||||
Logger.Debug("Powering off tv...");
|
||||
var task = PowerOffOnShutdownOrResume(PowerOnOffState.StandBy);
|
||||
Utils.WaitForTask(task);
|
||||
Logger.Debug("Done powering off tv");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.SetThreadExecutionState(NativeConstants.ES_CONTINUOUS);
|
||||
}
|
||||
}
|
||||
var devices = Devices.Where(d => d.PowerOffOnStandby);
|
||||
PowerOffDevices(devices, PowerOnOffState.StandBy);
|
||||
}
|
||||
|
||||
private void SessionEnded(object sender, SessionEndedEventArgs e)
|
||||
@@ -521,6 +488,22 @@ namespace ColorControl
|
||||
//}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -536,17 +519,19 @@ namespace ColorControl
|
||||
|
||||
public bool ShouldMonitorProcesses()
|
||||
{
|
||||
return Devices.Any(d => d.PowerSwitchOnScreenSaver) || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None));
|
||||
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.PowerSwitchOnScreenSaver && d.IsConnected());
|
||||
var wasConnected = Devices.Any(d => (d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver) && d.IsConnected());
|
||||
_monitorTaskCounter++;
|
||||
var validCounter = _monitorTaskCounter;
|
||||
var lastProcessId = 0;
|
||||
|
||||
var monitorContext = new ProcessMonitorContext();
|
||||
await Task.Delay(2000);
|
||||
|
||||
MonitorContext ??= new ProcessMonitorContext();
|
||||
//Process[] lastProcesses = null;
|
||||
|
||||
while (validCounter == _monitorTaskCounter)
|
||||
@@ -555,8 +540,8 @@ namespace ColorControl
|
||||
|
||||
try
|
||||
{
|
||||
var applicableDevices = Devices.Where(d => d.PowerSwitchOnScreenSaver || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
|
||||
((p.DeviceMacAddress == null && d == SelectedDevice) || p.DeviceMacAddress == d.MacAddress)));
|
||||
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())
|
||||
{
|
||||
@@ -621,78 +606,15 @@ namespace ColorControl
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
monitorContext.Devices = connectedDevices;
|
||||
monitorContext.RunningProcesses = processes;
|
||||
//if (lastProcesses != null)
|
||||
//{
|
||||
// monitorContext.StartedProcesses = processes.Where(p => !lastProcesses.Any(lp => lp.Id == p.Id)).ToArray();
|
||||
// monitorContext.StoppedProcesses = lastProcesses.Where(lp => !processes.Any(p => p.Id == lp.Id)).ToArray();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// monitorContext.StartedProcesses = null;
|
||||
// monitorContext.StoppedProcesses = null;
|
||||
//}
|
||||
|
||||
//if (monitorContext.StartedProcesses?.Any() ?? false)
|
||||
//{
|
||||
// Logger.Debug("Started processes: " + string.Join(", ", monitorContext.StartedProcesses.Select(p => p.ProcessName)));
|
||||
//}
|
||||
//lastProcesses = processes;
|
||||
|
||||
var process = processes.FirstOrDefault(p => p.ProcessName.ToLowerInvariant().EndsWith(".scr"));
|
||||
|
||||
if (process != null)
|
||||
if (connectedDevices.Any(d => d.PowerOffOnScreenSaver || d.PowerOnAfterScreenSaver))
|
||||
{
|
||||
var parent = process.Parent();
|
||||
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
|
||||
try
|
||||
{
|
||||
foreach (var device in connectedDevices)
|
||||
{
|
||||
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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ExecuteProcessPresets(monitorContext);
|
||||
lastProcessId = await HandleScreenSaverProcessAsync(lastProcessId, processes, connectedDevices);
|
||||
}
|
||||
|
||||
MonitorContext.Devices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
|
||||
MonitorContext.RunningProcesses = processes;
|
||||
|
||||
await ExecuteProcessPresets(MonitorContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -701,21 +623,100 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
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 presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == PresetTriggerType.ProcessSwitch));
|
||||
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 = Utils.IsNotificationDisabled();
|
||||
context.IsNotificationDisabled = FormUtils.IsNotificationDisabled();
|
||||
|
||||
var (processId, isFullScreen) = Utils.GetForegroundProcessIdAndIfFullScreen();
|
||||
var (processId, isFullScreen) = FormUtils.GetForegroundProcessIdAndIfFullScreen();
|
||||
|
||||
if (processId == Environment.ProcessId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (processId > 0)
|
||||
{
|
||||
context.ForegroundProcess = context.RunningProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
var process = context.RunningProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (process?.ProcessName?.Contains(".scr") ?? false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.ForegroundProcess = process;
|
||||
context.ForegroundProcessIsFullScreen = isFullScreen;
|
||||
}
|
||||
else
|
||||
@@ -750,9 +751,12 @@ namespace ColorControl
|
||||
|
||||
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,
|
||||
@@ -781,9 +785,11 @@ namespace ColorControl
|
||||
toApplyPreset = toApplyPresets.First();
|
||||
}
|
||||
|
||||
if (_lastAppliedPreset != toApplyPreset)
|
||||
if (_lastTriggeredPreset != toApplyPreset)
|
||||
{
|
||||
await ApplyPreset(toApplyPreset);
|
||||
|
||||
_lastTriggeredPreset = toApplyPreset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -799,7 +805,7 @@ namespace ColorControl
|
||||
|
||||
if (!string.IsNullOrEmpty(device.MacAddress))
|
||||
{
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress)).ToList();
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
presets.ForEach(preset =>
|
||||
{
|
||||
preset.DeviceMacAddress = null;
|
||||
@@ -811,5 +817,142 @@ namespace ColorControl
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ColorControl
|
||||
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 bool UseOldNpcapWol { get; set; }
|
||||
|
||||
public List<LgDevice> Devices { get; set; }
|
||||
|
||||
public bool ShowRemoteControl { get; set; }
|
||||
|
||||
public bool AllowFirmwareDowngrade { 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
+1
-1
@@ -1,5 +1,5 @@
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlForm
|
||||
{
|
||||
@@ -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
|
||||
{
|
||||
File renamed without changes.
+1
-1
@@ -1,5 +1,5 @@
|
||||
|
||||
namespace ColorControl
|
||||
namespace ColorControl.Services.LG
|
||||
{
|
||||
partial class RemoteControlPanel
|
||||
{
|
||||
+16
-2
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using ColorControl.Common;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
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;
|
||||
@@ -65,6 +68,17 @@ namespace ColorControl
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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,10 +23,15 @@ 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() : base()
|
||||
{
|
||||
@@ -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();
|
||||
|
||||
@@ -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,8 +93,7 @@ namespace ColorControl
|
||||
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
//return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" };
|
||||
return new[] { "Name", "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)
|
||||
@@ -90,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));
|
||||
@@ -106,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;
|
||||
@@ -141,7 +189,7 @@ namespace ColorControl
|
||||
return presets;
|
||||
}
|
||||
|
||||
public string GetTextForMenuItem()
|
||||
public override string GetTextForMenuItem()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
@@ -166,6 +214,11 @@ namespace ColorControl
|
||||
sb.AppendFormat("{0}Hz", refreshRate);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyResolution)
|
||||
{
|
||||
sb.AppendFormat("{0}x{1}", resolutionWidth, resolutionHeight);
|
||||
sb.Append(" / ");
|
||||
}
|
||||
if (applyDithering)
|
||||
{
|
||||
var dithering = GetDitheringDescription("No");
|
||||
@@ -177,6 +230,11 @@ namespace ColorControl
|
||||
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));
|
||||
@@ -218,6 +276,15 @@ namespace ColorControl
|
||||
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
|
||||
{
|
||||
@@ -12,6 +14,16 @@ namespace ColorControl
|
||||
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; }
|
||||
@@ -24,6 +36,22 @@ namespace ColorControl
|
||||
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)
|
||||
{
|
||||
@@ -50,6 +78,44 @@ namespace ColorControl
|
||||
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;
|
||||
@@ -102,6 +168,60 @@ namespace ColorControl
|
||||
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,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
|
||||
|
||||
+315
-45
@@ -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,18 +13,24 @@ namespace LgTv
|
||||
|
||||
public enum ControlButtons
|
||||
{
|
||||
Back,Down,Left,Right,
|
||||
Back, Down, Left, Right,
|
||||
OK,
|
||||
Exit
|
||||
}
|
||||
|
||||
public enum PictureMode
|
||||
{
|
||||
cinema, eco, expert1, expert2, game,
|
||||
normal, photo, sports, technicolor, vivid, hdrEffect, hdrCinema,
|
||||
hdrCinemaBright, hdrExternal, hdrGame, hdrStandard, hdrTechnicolor,
|
||||
hdrVivid, dolbyHdrCinema, dolbyHdrCinemaBright, dolbyHdrDarkAmazon,
|
||||
dolbyHdrGame, dolbyHdrStandard, dolbyHdrVivid, dolbyStandard
|
||||
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
|
||||
@@ -70,7 +77,8 @@ namespace LgTv
|
||||
auto,
|
||||
extended,
|
||||
wide,
|
||||
srgb
|
||||
srgb,
|
||||
native
|
||||
}
|
||||
|
||||
public enum EnergySaving
|
||||
@@ -93,10 +101,173 @@ namespace LgTv
|
||||
public enum TruMotionMode
|
||||
{
|
||||
off,
|
||||
smooth,
|
||||
clear,
|
||||
clearPlus,
|
||||
user
|
||||
}
|
||||
|
||||
public class LgTvApi:IDisposable
|
||||
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; }
|
||||
|
||||
@@ -160,7 +331,7 @@ namespace LgTv
|
||||
_keyStore.SaveClientKey(result.clientKey);
|
||||
_keyStore.SaveHandShake(registerJson);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<bool> Connect()
|
||||
{
|
||||
@@ -174,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;
|
||||
@@ -208,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();
|
||||
@@ -225,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);
|
||||
@@ -236,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);
|
||||
@@ -257,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)
|
||||
{
|
||||
@@ -265,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"));
|
||||
}
|
||||
@@ -287,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"));
|
||||
}
|
||||
@@ -316,7 +492,7 @@ namespace LgTv
|
||||
//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()
|
||||
@@ -336,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)
|
||||
{
|
||||
@@ -353,7 +529,7 @@ 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);
|
||||
@@ -373,11 +549,18 @@ namespace LgTv
|
||||
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"" ]";
|
||||
var keys = @"[ ""contrast"", ""backlight"", ""brightness"", ""color"" ]";
|
||||
|
||||
var json = @"{ ""category"": ""picture"", ""keys"": " + keys + @", ""subscribe"": true }";
|
||||
|
||||
@@ -491,18 +674,7 @@ namespace LgTv
|
||||
|
||||
public async Task SetSystemSettings(string key, object value, string category = "picture")
|
||||
{
|
||||
string jsonValue;
|
||||
var valueType = value.GetType();
|
||||
var intType = typeof(int);
|
||||
if (valueType.IsArray && intType.IsAssignableFrom(valueType.GetElementType()))
|
||||
{
|
||||
var values = ((Array)value).Cast<int>();
|
||||
jsonValue = "[" + string.Join(", ", values.Select(x => x.ToString()).ToArray()) + ']';
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonValue = $"\"{value}\"";
|
||||
}
|
||||
var jsonValue = ParamToJson(value, ref key);
|
||||
|
||||
var lunauri = "luna://com.webos.settingsservice/setSystemSettings";
|
||||
|
||||
@@ -513,20 +685,101 @@ namespace LgTv
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task SetConfig(string key, string value)
|
||||
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 @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": """ + value + @""" } }");
|
||||
var jsonValue = ParamToJson(value, ref key);
|
||||
|
||||
var @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": " + jsonValue + @" } }");
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task SetConfig(string key, bool value)
|
||||
public async Task SetSystemProperty(string key, object value)
|
||||
{
|
||||
var lunauri = "luna://com.webos.service.config/setConfigs";
|
||||
var lunauri = "luna://com.webos.service.tv.systemproperty/setProperties";
|
||||
|
||||
var @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": " + value.ToString().ToLowerInvariant() + @" } }");
|
||||
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);
|
||||
}
|
||||
@@ -592,13 +845,30 @@ namespace LgTv
|
||||
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()
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
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 Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ColorControl;
|
||||
|
||||
namespace LgTv
|
||||
{
|
||||
@@ -18,7 +18,7 @@ namespace LgTv
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class LgTvApiCore : IDisposable
|
||||
{
|
||||
public bool ConnectionClosed { get; private set; }
|
||||
@@ -41,32 +41,21 @@ 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;
|
||||
using (var cancellationTokenSource = new CancellationTokenSource(5000))
|
||||
|
||||
try
|
||||
{
|
||||
var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token);
|
||||
var result = await connectTask.ContinueWith((antecedent) =>
|
||||
{
|
||||
if (antecedent.Status == TaskStatus.RanToCompletion)
|
||||
{
|
||||
// connectTask ran to completion, so we know that the MessageWebSocket is connected.
|
||||
// Add additional code here to use the MessageWebSocket.
|
||||
IsConnected?.Invoke(true);
|
||||
await _connection.ConnectAsync(uri).AsTask().WaitAsync(TimeSpan.FromSeconds(5));
|
||||
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// connectTask timed out, or faulted.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
_messageWriter = new DataWriter(_connection.OutputStream);
|
||||
|
||||
return result;
|
||||
IsConnected?.Invoke(true);
|
||||
return true;
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -88,6 +77,11 @@ namespace LgTv
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_messageWriter == null)
|
||||
{
|
||||
throw new Exception("SendMessageAsync: no writer");
|
||||
}
|
||||
|
||||
_messageWriter.WriteString(message);
|
||||
|
||||
using (var cancellationTokenSource = new CancellationTokenSource(5000))
|
||||
@@ -126,9 +120,8 @@ namespace LgTv
|
||||
{
|
||||
throw new Exception("Connection closed");
|
||||
}
|
||||
var result = await taskSource.Task.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
return await taskSource.Task;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -41,7 +41,21 @@ namespace LgTv
|
||||
SAP,
|
||||
PROGRAM,
|
||||
PLAY,
|
||||
PAUSE
|
||||
PAUSE,
|
||||
STOP,
|
||||
REWIND,
|
||||
FASTFORWARD,
|
||||
GUIDE,
|
||||
AMAZON,
|
||||
NETFLIX,
|
||||
MAGNIFIER_ZOOM,
|
||||
LIVE_ZOOM,
|
||||
_3D_MODE,
|
||||
ASPECT_RATIO,
|
||||
RECENT,
|
||||
RECORD,
|
||||
SCREEN_REMOTE,
|
||||
MYAPPS
|
||||
}
|
||||
|
||||
public class LgWebOsMouseService : IDisposable
|
||||
@@ -59,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)
|
||||
{
|
||||
@@ -68,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
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.7" targetFramework="net472" />
|
||||
<package id="Native" version="0.1.1" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
<package id="NLog" version="4.7.10" targetFramework="net472" />
|
||||
<package id="NStandard" version="0.8.9" targetFramework="net472" />
|
||||
<package id="NvAPIWrapper.Net" version="0.8.1.101" targetFramework="net472" />
|
||||
<package id="NWin32" version="1.2.0" 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using nspector.Common.Helper;
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Text.RegularExpressions;
|
||||
using nvw = nspector.Native.NVAPI2.NvapiDrsWrapper;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
|
||||
|
||||
public class DrsScannerService : DrsSettingsServiceBase
|
||||
{
|
||||
|
||||
public DrsScannerService(DrsSettingsMetaService metaService, DrsDecrypterService decrpterService)
|
||||
: base(metaService, decrpterService)
|
||||
{ }
|
||||
|
||||
|
||||
internal List<CachedSettings> CachedSettings = new List<CachedSettings>();
|
||||
internal List<string> ModifiedProfiles = new List<string>();
|
||||
internal HashSet<string> UserProfiles = new HashSet<string>();
|
||||
|
||||
// most common setting ids as start pattern for the heuristic scan
|
||||
private readonly uint[] _commonSettingIds = new uint[] { 0x1095DEF8, 0x1033DCD2, 0x1033CEC1,
|
||||
0x10930F46, 0x00A06946, 0x10ECDB82, 0x20EBD7B8, 0x0095DEF9, 0x00D55F7D,
|
||||
0x1033DCD3, 0x1033CEC2, 0x2072F036, 0x00664339, 0x002C7F45, 0x209746C1,
|
||||
0x0076E164, 0x20FF7493, 0x204CFF7B };
|
||||
|
||||
|
||||
private bool CheckCommonSetting(IntPtr hSession, IntPtr hProfile, NVDRS_PROFILE profile,
|
||||
ref int checkedSettingsCount, uint checkSettingId, bool addToScanResult,
|
||||
ref List<uint> alreadyCheckedSettingIds)
|
||||
{
|
||||
|
||||
if (checkedSettingsCount >= profile.numOfSettings)
|
||||
return false;
|
||||
|
||||
var setting = new NVDRS_SETTING();
|
||||
setting.version = nvw.NVDRS_SETTING_VER;
|
||||
|
||||
if (nvw.DRS_GetSetting(hSession, hProfile, checkSettingId, ref setting) != NvAPI_Status.NVAPI_OK)
|
||||
return false;
|
||||
|
||||
if (setting.settingLocation != NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
return false;
|
||||
|
||||
if (!addToScanResult && setting.isCurrentPredefined == 1)
|
||||
{
|
||||
checkedSettingsCount++;
|
||||
}
|
||||
else if (addToScanResult)
|
||||
{
|
||||
if (decrypter != null)
|
||||
{
|
||||
decrypter.DecryptSettingIfNeeded(profile.profileName, ref setting);
|
||||
}
|
||||
|
||||
checkedSettingsCount++;
|
||||
AddScannedSettingToCache(profile, setting);
|
||||
alreadyCheckedSettingIds.Add(setting.settingId);
|
||||
return (setting.isCurrentPredefined != 1);
|
||||
}
|
||||
else if (setting.isCurrentPredefined != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private int CalcPercent(int current, int max)
|
||||
{
|
||||
return (current > 0) ? (int)Math.Round((current * 100f) / max) : 0; ;
|
||||
}
|
||||
|
||||
public async Task ScanProfileSettingsAsync(bool justModified, IProgress<int> progress, CancellationToken token = default(CancellationToken))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
ModifiedProfiles = new List<string>();
|
||||
UserProfiles = new HashSet<string>();
|
||||
var knownPredefines = new List<uint>(_commonSettingIds);
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
IntPtr hBaseProfile = GetProfileHandle(hSession, "");
|
||||
var profileHandles = EnumProfileHandles(hSession);
|
||||
|
||||
var maxProfileCount = profileHandles.Count;
|
||||
int curProfilePos = 0;
|
||||
|
||||
foreach (IntPtr hProfile in profileHandles)
|
||||
{
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
progress?.Report(CalcPercent(curProfilePos++, maxProfileCount));
|
||||
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
|
||||
int checkedSettingsCount = 0;
|
||||
var alreadyChecked = new List<uint>();
|
||||
|
||||
bool foundModifiedProfile = false;
|
||||
if (profile.isPredefined == 0)
|
||||
{
|
||||
ModifiedProfiles.Add(profile.profileName);
|
||||
UserProfiles.Add(profile.profileName);
|
||||
foundModifiedProfile = true;
|
||||
if (justModified) continue;
|
||||
}
|
||||
|
||||
|
||||
foreach (uint kpd in knownPredefines)
|
||||
{
|
||||
if (CheckCommonSetting(hSession, hProfile, profile,
|
||||
ref checkedSettingsCount, kpd, !justModified, ref alreadyChecked))
|
||||
{
|
||||
if (!foundModifiedProfile)
|
||||
{
|
||||
foundModifiedProfile = true;
|
||||
ModifiedProfiles.Add(profile.profileName);
|
||||
if (justModified) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((foundModifiedProfile && justModified) || checkedSettingsCount >= profile.numOfSettings)
|
||||
continue;
|
||||
|
||||
var settings = GetProfileSettings(hSession, hProfile);
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
if (knownPredefines.IndexOf(setting.settingId) < 0)
|
||||
knownPredefines.Add(setting.settingId);
|
||||
|
||||
if (!justModified && alreadyChecked.IndexOf(setting.settingId) < 0)
|
||||
AddScannedSettingToCache(profile, setting);
|
||||
|
||||
if (setting.isCurrentPredefined != 1)
|
||||
{
|
||||
if (!foundModifiedProfile)
|
||||
{
|
||||
foundModifiedProfile = true;
|
||||
ModifiedProfiles.Add(profile.profileName);
|
||||
if (justModified) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void AddScannedSettingToCache(NVDRS_PROFILE profile, NVDRS_SETTING setting)
|
||||
{
|
||||
// Skip 3D Vision string values here for improved scan performance
|
||||
bool allowAddValue = setting.settingId < 0x70000000 || setting.settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE;
|
||||
|
||||
//bool allowAddValue = true;
|
||||
|
||||
var cachedSetting = CachedSettings
|
||||
.FirstOrDefault(x => x.SettingId.Equals(setting.settingId));
|
||||
|
||||
bool cacheEntryExists = true;
|
||||
if (cachedSetting == null)
|
||||
{
|
||||
cacheEntryExists = false;
|
||||
cachedSetting = new CachedSettings(setting.settingId, setting.settingType);
|
||||
}
|
||||
|
||||
if (setting.isPredefinedValid == 1)
|
||||
{
|
||||
if (allowAddValue)
|
||||
{
|
||||
if (setting.settingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE)
|
||||
cachedSetting.AddStringValue(setting.predefinedValue.stringValue, profile.profileName);
|
||||
else if (setting.settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE)
|
||||
cachedSetting.AddDwordValue(setting.predefinedValue.dwordValue, profile.profileName);
|
||||
else if (setting.settingType == NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE)
|
||||
cachedSetting.AddBinaryValue(setting.predefinedValue.binaryValue, profile.profileName);
|
||||
}
|
||||
else
|
||||
cachedSetting.ProfileCount++;
|
||||
|
||||
if (!cacheEntryExists)
|
||||
CachedSettings.Add(cachedSetting);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public string FindProfilesUsingApplication(string applicationName)
|
||||
{
|
||||
string lowerApplicationName = applicationName.ToLower();
|
||||
string tmpfile = TempFile.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
var matchingProfiles = new List<string>();
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
SaveSettingsFileEx(hSession, tmpfile);
|
||||
});
|
||||
|
||||
if (File.Exists(tmpfile))
|
||||
{
|
||||
string content = File.ReadAllText(tmpfile);
|
||||
string pattern = "\\sProfile\\s\\\"(?<profile>.*?)\\\"(?<scope>.*?Executable.*?)EndProfile";
|
||||
foreach (Match m in Regex.Matches(content, pattern, RegexOptions.Singleline))
|
||||
{
|
||||
string scope = m.Result("${scope}");
|
||||
foreach (Match ms in Regex.Matches(scope, "Executable\\s\\\"(?<app>.*?)\\\"", RegexOptions.Singleline))
|
||||
{
|
||||
if (ms.Result("${app}").ToLower() == lowerApplicationName)
|
||||
{
|
||||
matchingProfiles.Add(m.Result("${profile}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(";", matchingProfiles);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tmpfile))
|
||||
File.Delete(tmpfile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using nspector.Common.CustomSettings;
|
||||
using System.Reflection;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsServiceLocator
|
||||
{
|
||||
private static readonly CustomSettingNames CustomSettings;
|
||||
public static readonly CustomSettingNames ReferenceSettings;
|
||||
public static readonly DrsSettingsMetaService MetaService;
|
||||
public static readonly DrsImportService ImportService;
|
||||
public static readonly DrsSettingsService SettingService;
|
||||
public static readonly DrsScannerService ScannerService;
|
||||
public static readonly DrsDecrypterService DecrypterService;
|
||||
|
||||
static DrsServiceLocator()
|
||||
{
|
||||
CustomSettings = LoadCustomSettings();
|
||||
ReferenceSettings = LoadReferenceSettings();
|
||||
|
||||
MetaService = new DrsSettingsMetaService(CustomSettings, ReferenceSettings);
|
||||
DecrypterService = new DrsDecrypterService(MetaService);
|
||||
ScannerService = new DrsScannerService(MetaService, DecrypterService);
|
||||
SettingService = new DrsSettingsService(MetaService, DecrypterService);
|
||||
ImportService = new DrsImportService(MetaService, SettingService, ScannerService, DecrypterService);
|
||||
}
|
||||
|
||||
private static CustomSettingNames LoadCustomSettings()
|
||||
{
|
||||
string csnDefaultPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\nspector\\CustomSettingNames.xml";
|
||||
|
||||
if (File.Exists(csnDefaultPath))
|
||||
return CustomSettingNames.FactoryLoadFromFile(csnDefaultPath);
|
||||
else
|
||||
return new CustomSettingNames(); //return CustomSettingNames.FactoryLoadFromString(Properties.Resources.CustomSettingNames);
|
||||
}
|
||||
|
||||
private static CustomSettingNames LoadReferenceSettings()
|
||||
{
|
||||
string csnDefaultPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\nspector\\Reference.xml";
|
||||
|
||||
if (File.Exists(csnDefaultPath))
|
||||
return CustomSettingNames.FactoryLoadFromFile(csnDefaultPath);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using nspector.Native.NVAPI2;
|
||||
using System;
|
||||
using nvw = nspector.Native.NVAPI2.NvapiDrsWrapper;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsSessionScope
|
||||
{
|
||||
|
||||
public static volatile IntPtr GlobalSession;
|
||||
|
||||
public static volatile bool HoldSession = true;
|
||||
|
||||
private static object _Sync = new object();
|
||||
|
||||
|
||||
public static T DrsSession<T>(Func<IntPtr, T> action, bool forceNonGlobalSession = false, bool preventLoadSettings = false)
|
||||
{
|
||||
lock (_Sync)
|
||||
{
|
||||
if (!HoldSession || forceNonGlobalSession)
|
||||
return NonGlobalDrsSession<T>(action, preventLoadSettings);
|
||||
|
||||
|
||||
if (GlobalSession == IntPtr.Zero)
|
||||
{
|
||||
|
||||
#pragma warning disable CS0420
|
||||
var csRes = nvw.DRS_CreateSession(ref GlobalSession);
|
||||
#pragma warning restore CS0420
|
||||
|
||||
if (csRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_CreateSession", csRes);
|
||||
|
||||
if (!preventLoadSettings)
|
||||
{
|
||||
var nvRes = nvw.DRS_LoadSettings(GlobalSession);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_LoadSettings", nvRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalSession != IntPtr.Zero)
|
||||
{
|
||||
return action(GlobalSession);
|
||||
}
|
||||
|
||||
throw new Exception(nameof(GlobalSession) + " is Zero!");
|
||||
}
|
||||
|
||||
public static void DestroyGlobalSession()
|
||||
{
|
||||
lock (_Sync)
|
||||
{
|
||||
if (GlobalSession != IntPtr.Zero)
|
||||
{
|
||||
var csRes = nvw.DRS_DestroySession(GlobalSession);
|
||||
GlobalSession = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T NonGlobalDrsSession<T>(Func<IntPtr, T> action, bool preventLoadSettings = false)
|
||||
{
|
||||
IntPtr hSession = IntPtr.Zero;
|
||||
var csRes = nvw.DRS_CreateSession(ref hSession);
|
||||
if (csRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_CreateSession", csRes);
|
||||
|
||||
try
|
||||
{
|
||||
if (!preventLoadSettings)
|
||||
{
|
||||
var nvRes = nvw.DRS_LoadSettings(hSession);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_LoadSettings", nvRes);
|
||||
}
|
||||
|
||||
return action(hSession);
|
||||
}
|
||||
finally
|
||||
{
|
||||
var nvRes = nvw.DRS_DestroySession(hSession);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_DestroySession", nvRes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
using nspector.Common.CustomSettings;
|
||||
using nspector.Common.Meta;
|
||||
using nspector.Native.NVAPI2;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsSettingsMetaService
|
||||
{
|
||||
|
||||
private ISettingMetaService ConstantMeta;
|
||||
private ISettingMetaService CustomMeta;
|
||||
public ISettingMetaService DriverMeta;
|
||||
private ISettingMetaService ScannedMeta;
|
||||
private ISettingMetaService ReferenceMeta;
|
||||
|
||||
private readonly CustomSettingNames _customSettings;
|
||||
private readonly CustomSettingNames _referenceSettings;
|
||||
|
||||
private List<MetaServiceItem> MetaServices = new List<MetaServiceItem>();
|
||||
|
||||
private Dictionary<uint, SettingMeta> settingMetaCache = new Dictionary<uint, SettingMeta>();
|
||||
|
||||
public DrsSettingsMetaService(CustomSettingNames customSettings, CustomSettingNames referenceSettings = null)
|
||||
{
|
||||
_customSettings = customSettings;
|
||||
_referenceSettings = referenceSettings;
|
||||
|
||||
ResetMetaCache(true);
|
||||
}
|
||||
|
||||
public void ResetMetaCache(bool initOnly = false)
|
||||
{
|
||||
settingMetaCache = new Dictionary<uint, SettingMeta>();
|
||||
MetaServices = new List<MetaServiceItem>();
|
||||
|
||||
CustomMeta = new CustomSettingMetaService(_customSettings);
|
||||
MetaServices.Add(new MetaServiceItem() { ValueNamePrio = 1, Service = CustomMeta });
|
||||
|
||||
if (!initOnly)
|
||||
{
|
||||
DriverMeta = new DriverSettingMetaService();
|
||||
MetaServices.Add(new MetaServiceItem() { ValueNamePrio = 5, Service = DriverMeta });
|
||||
|
||||
ConstantMeta = new ConstantSettingMetaService();
|
||||
MetaServices.Add(new MetaServiceItem() { ValueNamePrio = 2, Service = ConstantMeta });
|
||||
|
||||
if (DrsServiceLocator.ScannerService != null)
|
||||
{
|
||||
ScannedMeta = new ScannedSettingMetaService(DrsServiceLocator.ScannerService.CachedSettings);
|
||||
MetaServices.Add(new MetaServiceItem() { ValueNamePrio = 3, Service = ScannedMeta });
|
||||
}
|
||||
|
||||
if (_referenceSettings != null)
|
||||
{
|
||||
ReferenceMeta = new CustomSettingMetaService(_referenceSettings, SettingMetaSource.ReferenceSettings);
|
||||
MetaServices.Add(new MetaServiceItem() { ValueNamePrio = 4, Service = ReferenceMeta });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private NVDRS_SETTING_TYPE? GetSettingValueType(uint settingId)
|
||||
{
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var settingValueType = service.Service.GetSettingValueType(settingId);
|
||||
if (settingValueType != null)
|
||||
return settingValueType.Value;
|
||||
}
|
||||
|
||||
return NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE;
|
||||
}
|
||||
|
||||
private string GetSettingName(uint settingId)
|
||||
{
|
||||
string hexCandidate = null;
|
||||
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var settingName = service.Service.GetSettingName(settingId);
|
||||
|
||||
if (!string.IsNullOrEmpty(settingName))
|
||||
{
|
||||
if (settingName.StartsWith("0x"))
|
||||
{
|
||||
hexCandidate = settingName;
|
||||
continue;
|
||||
}
|
||||
return settingName;
|
||||
}
|
||||
}
|
||||
return hexCandidate;
|
||||
}
|
||||
|
||||
private string GetGroupName(uint settingId)
|
||||
{
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var groupName = service.Service.GetGroupName(settingId);
|
||||
if (groupName != null)
|
||||
return groupName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private uint GetDwordDefaultValue(uint settingId)
|
||||
{
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var settingDefault = service.Service.GetDwordDefaultValue(settingId);
|
||||
if (settingDefault != null)
|
||||
return settingDefault.Value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private string GetStringDefaultValue(uint settingId)
|
||||
{
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var settingDefault = service.Service.GetStringDefaultValue(settingId);
|
||||
if (settingDefault != null)
|
||||
return settingDefault;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] GetBinaryDefaultValue(uint settingId)
|
||||
{
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
var settingDefault = service.Service.GetBinaryDefaultValue(settingId);
|
||||
if (settingDefault != null)
|
||||
return settingDefault;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<SettingValue<T>> MergeSettingValues<T>(List<SettingValue<T>> a, List<SettingValue<T>> b)
|
||||
{
|
||||
if (b == null)
|
||||
return a;
|
||||
|
||||
// force scanned settings to add instead of merge
|
||||
var isScannedValuesList = (b.Count > 0 && b.First().ValueSource == SettingMetaSource.ScannedSettings);
|
||||
if (isScannedValuesList)
|
||||
{
|
||||
a.AddRange(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentNonScannedValues = a.Where(xa => xa.ValueSource != SettingMetaSource.ScannedSettings).Select(xa => xa.Value).ToList();
|
||||
|
||||
var newNonScannedValues = b.Where(xb => !currentNonScannedValues.Contains(xb.Value)).ToList();
|
||||
a.AddRange(newNonScannedValues);
|
||||
|
||||
foreach (var settingValue in a)
|
||||
{
|
||||
var bVal = b.FirstOrDefault(x => x.Value.Equals(settingValue.Value) && settingValue.ValueSource != SettingMetaSource.ScannedSettings);
|
||||
if (bVal != null && bVal.ValueName != null)
|
||||
{
|
||||
settingValue.ValueName = bVal.ValueName;
|
||||
settingValue.ValueSource = bVal.ValueSource;
|
||||
settingValue.ValuePos = bVal.ValuePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var atmp = a.FirstOrDefault();
|
||||
if (atmp != null && atmp is IComparable)
|
||||
return a.OrderBy(x => x.Value).ToList();
|
||||
else
|
||||
return a.ToList();
|
||||
}
|
||||
|
||||
private List<SettingValue<byte[]>> GetBinaryValues(uint settingId)
|
||||
{
|
||||
var result = new List<SettingValue<byte[]>>();
|
||||
|
||||
foreach (var service in MetaServices.OrderByDescending(x => x.ValueNamePrio))
|
||||
{
|
||||
result = MergeSettingValues(result, service.Service.GetBinaryValues(settingId));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SettingValue<string>> GetStringValues(uint settingId)
|
||||
{
|
||||
var result = new List<SettingValue<string>>();
|
||||
|
||||
foreach (var service in MetaServices.OrderByDescending(x => x.ValueNamePrio))
|
||||
{
|
||||
result = MergeSettingValues(result, service.Service.GetStringValues(settingId));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SettingValue<uint>> GetDwordValues(uint settingId)
|
||||
{
|
||||
var result = new List<SettingValue<uint>>();
|
||||
|
||||
foreach (var service in MetaServices.OrderByDescending(x => x.ValueNamePrio))
|
||||
{
|
||||
result = MergeSettingValues(result, service.Service.GetDwordValues(settingId));
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
result = (from v in result.Where(x => 1 == 1
|
||||
&& !x.ValueName.EndsWith("_NUM")
|
||||
&& !x.ValueName.EndsWith("_MASK")
|
||||
&& (!x.ValueName.EndsWith("_MIN") || x.ValueName.Equals("PREFERRED_PSTATE_PREFER_MIN"))
|
||||
&& (!x.ValueName.EndsWith("_MAX") || x.ValueName.Equals("PREFERRED_PSTATE_PREFER_MAX"))
|
||||
|
||||
)
|
||||
group v by v.ValueName into g
|
||||
select g.First(t => t.ValueName == g.Key))
|
||||
.OrderBy(v => v.ValueSource)
|
||||
.ThenBy(v => v.ValuePos)
|
||||
.ThenBy(v => v.ValueName).ToList();
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<uint> GetSettingIds(SettingViewMode viewMode)
|
||||
{
|
||||
var settingIds = new List<uint>();
|
||||
var allowedSourcesForViewMode = GetAllowedSettingIdMetaSourcesForViewMode(viewMode);
|
||||
|
||||
foreach (var service in MetaServices.OrderBy(x => x.Service.Source))
|
||||
{
|
||||
if (allowedSourcesForViewMode.Contains(service.Service.Source))
|
||||
{
|
||||
settingIds.AddRange(service.Service.GetSettingIds());
|
||||
}
|
||||
}
|
||||
return settingIds.Distinct().ToList();
|
||||
}
|
||||
|
||||
private SettingMetaSource[] GetAllowedSettingIdMetaSourcesForViewMode(SettingViewMode viewMode)
|
||||
{
|
||||
switch (viewMode)
|
||||
{
|
||||
case SettingViewMode.CustomSettingsOnly:
|
||||
return new[] {
|
||||
SettingMetaSource.CustomSettings
|
||||
};
|
||||
case SettingViewMode.IncludeScannedSetttings:
|
||||
return new[] {
|
||||
SettingMetaSource.ConstantSettings,
|
||||
SettingMetaSource.ScannedSettings,
|
||||
SettingMetaSource.CustomSettings,
|
||||
SettingMetaSource.DriverSettings,
|
||||
SettingMetaSource.ReferenceSettings,
|
||||
};
|
||||
default:
|
||||
return new[] {
|
||||
SettingMetaSource.CustomSettings,
|
||||
SettingMetaSource.DriverSettings,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private SettingMetaSource[] GetAllowedSettingValueMetaSourcesForViewMode(SettingViewMode viewMode)
|
||||
{
|
||||
switch (viewMode)
|
||||
{
|
||||
case SettingViewMode.CustomSettingsOnly:
|
||||
return new[] {
|
||||
SettingMetaSource.CustomSettings,
|
||||
SettingMetaSource.ScannedSettings,
|
||||
};
|
||||
default:
|
||||
return new[] {
|
||||
SettingMetaSource.ConstantSettings,
|
||||
SettingMetaSource.ScannedSettings,
|
||||
SettingMetaSource.CustomSettings,
|
||||
SettingMetaSource.DriverSettings,
|
||||
SettingMetaSource.ReferenceSettings,
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private SettingMeta CreateSettingMeta(uint settingId)
|
||||
{
|
||||
var settingType = GetSettingValueType(settingId);
|
||||
var settingName = GetSettingName(settingId);
|
||||
var groupName = GetGroupName(settingId);
|
||||
|
||||
if (groupName == null)
|
||||
groupName = GetLegacyGroupName(settingId, settingName);
|
||||
|
||||
|
||||
|
||||
var result = new SettingMeta()
|
||||
{
|
||||
SettingType = settingType,
|
||||
SettingName = settingName,
|
||||
GroupName = groupName,
|
||||
|
||||
IsApiExposed = GetIsApiExposed(settingId),
|
||||
|
||||
DefaultDwordValue =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE
|
||||
? GetDwordDefaultValue(settingId) : 0,
|
||||
|
||||
DefaultStringValue =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE
|
||||
? GetStringDefaultValue(settingId) : null,
|
||||
|
||||
DefaultBinaryValue =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE
|
||||
? GetBinaryDefaultValue(settingId) : null,
|
||||
|
||||
DwordValues =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE
|
||||
? GetDwordValues(settingId) : null,
|
||||
|
||||
StringValues =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE
|
||||
? GetStringValues(settingId) : null,
|
||||
|
||||
BinaryValues =
|
||||
settingType == NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE
|
||||
? GetBinaryValues(settingId) : null,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SettingMeta PostProcessMeta(uint settingId, SettingMeta settingMeta, SettingViewMode viewMode)
|
||||
{
|
||||
var newMeta = new SettingMeta()
|
||||
{
|
||||
DefaultDwordValue = settingMeta.DefaultDwordValue,
|
||||
DefaultStringValue = settingMeta.DefaultStringValue,
|
||||
DefaultBinaryValue = settingMeta.DefaultBinaryValue,
|
||||
SettingName = settingMeta.SettingName,
|
||||
SettingType = settingMeta.SettingType,
|
||||
GroupName = settingMeta.GroupName,
|
||||
IsApiExposed = settingMeta.IsApiExposed,
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(newMeta.SettingName))
|
||||
{
|
||||
newMeta.SettingName = string.Format("0x{0:X8}", settingId);
|
||||
}
|
||||
|
||||
var allowedSourcesForViewMode = GetAllowedSettingValueMetaSourcesForViewMode(viewMode);
|
||||
if (settingMeta.DwordValues != null)
|
||||
{
|
||||
newMeta.DwordValues = settingMeta.DwordValues
|
||||
.Where(x => allowedSourcesForViewMode.Contains(x.ValueSource)).ToList();
|
||||
}
|
||||
|
||||
if (settingMeta.StringValues != null)
|
||||
{
|
||||
newMeta.StringValues = settingMeta.StringValues
|
||||
.Where(x => allowedSourcesForViewMode.Contains(x.ValueSource)).ToList();
|
||||
}
|
||||
|
||||
if (settingMeta.BinaryValues != null)
|
||||
{
|
||||
newMeta.BinaryValues = settingMeta.BinaryValues
|
||||
.Where(x => allowedSourcesForViewMode.Contains(x.ValueSource)).ToList();
|
||||
}
|
||||
|
||||
return newMeta;
|
||||
}
|
||||
|
||||
public SettingMeta GetSettingMeta(uint settingId, SettingViewMode viewMode = SettingViewMode.Normal)
|
||||
{
|
||||
if (settingMetaCache.ContainsKey(settingId))
|
||||
{
|
||||
return PostProcessMeta(settingId, settingMetaCache[settingId], viewMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
var settingMeta = CreateSettingMeta(settingId);
|
||||
settingMetaCache.Add(settingId, settingMeta);
|
||||
return PostProcessMeta(settingId, settingMeta, viewMode);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLegacyGroupName(uint settingId, string settingName)
|
||||
{
|
||||
if (settingName == null)
|
||||
return null;
|
||||
|
||||
if (settingName.ToUpper().Contains("SLI"))
|
||||
return "6 - SLI";
|
||||
else if (settingName.ToUpper().Contains("STEREO"))
|
||||
return "7 - Stereo";
|
||||
else if (settingName.StartsWith("0x"))
|
||||
return "Unknown";
|
||||
else
|
||||
return "Other";
|
||||
|
||||
}
|
||||
private bool GetIsApiExposed(uint settingId)
|
||||
{
|
||||
var driverMeta = MetaServices.FirstOrDefault(m => m.Service.Source == SettingMetaSource.DriverSettings);
|
||||
return (driverMeta != null && driverMeta.Service.GetSettingIds().Contains(settingId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
using nspector.Common.Helper;
|
||||
using nspector.Common.Meta;
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using nvw = nspector.Native.NVAPI2.NvapiDrsWrapper;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public class DrsSettingsService : DrsSettingsServiceBase
|
||||
{
|
||||
|
||||
public DrsSettingsService(DrsSettingsMetaService metaService, DrsDecrypterService decrpterService)
|
||||
: base(metaService, decrpterService)
|
||||
{
|
||||
_baseProfileSettingIds = InitBaseProfileSettingIds();
|
||||
}
|
||||
|
||||
private List<uint> InitBaseProfileSettingIds()
|
||||
{
|
||||
return DrsSession((hSession) =>
|
||||
{
|
||||
var hBaseProfile = GetProfileHandle(hSession, "");
|
||||
var baseProfileSettings = GetProfileSettings(hSession, hBaseProfile);
|
||||
|
||||
return baseProfileSettings.Select(x => x.settingId).ToList();
|
||||
});
|
||||
}
|
||||
|
||||
private readonly List<uint> _baseProfileSettingIds;
|
||||
|
||||
private string GetDrsProgramPath()
|
||||
{
|
||||
var nvidiaInstallerFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"NVIDIA Corporation\Installer2");
|
||||
var driverFolders = Directory.EnumerateDirectories(nvidiaInstallerFolder, "Display.Driver.*");
|
||||
foreach (var folder in driverFolders)
|
||||
{
|
||||
var fiDbInstaller = new FileInfo(Path.Combine(folder, "dbInstaller.exe"));
|
||||
if (!fiDbInstaller.Exists) continue;
|
||||
|
||||
var fviDbInstaller = FileVersionInfo.GetVersionInfo(fiDbInstaller.FullName);
|
||||
|
||||
var fileversion = fviDbInstaller.FileVersion.Replace(".", "");
|
||||
var driverver = DriverVersion.ToString().Replace(",", "").Replace(".", "");
|
||||
|
||||
if (fileversion.EndsWith(driverver))
|
||||
return fiDbInstaller.DirectoryName;
|
||||
}
|
||||
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||
@"NVIDIA Corporation\Drs");
|
||||
}
|
||||
|
||||
private void RunDrsInitProcess()
|
||||
{
|
||||
var drsPath = GetDrsProgramPath();
|
||||
|
||||
var si = new ProcessStartInfo();
|
||||
si.UseShellExecute = true;
|
||||
si.WorkingDirectory = drsPath;
|
||||
si.Arguments = "-init";
|
||||
si.FileName = Path.Combine(drsPath, "dbInstaller.exe");
|
||||
if (!AdminHelper.IsAdmin)
|
||||
si.Verb = "runas";
|
||||
var p = Process.Start(si);
|
||||
p.WaitForExit();
|
||||
}
|
||||
|
||||
public void DeleteAllProfilesHard()
|
||||
{
|
||||
var tmpFile = TempFile.GetTempFileName();
|
||||
try
|
||||
{
|
||||
File.WriteAllText(tmpFile, "BaseProfile \"Base Profile\"\r\nSelectedGlobalProfile \"Base Profile\"\r\nProfile \"Base Profile\"\r\nShowOn All\r\nProfileType Global\r\nEndProfile\r\n");
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
LoadSettingsFileEx(hSession, tmpFile);
|
||||
SaveSettings(hSession);
|
||||
}, forceNonGlobalSession: true, preventLoadSettings: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tmpFile))
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void DeleteProfileHard(string profileName)
|
||||
{
|
||||
var tmpFileName = TempFile.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
var tmpFileContent = "";
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
SaveSettingsFileEx(hSession, tmpFileName);
|
||||
tmpFileContent = File.ReadAllText(tmpFileName);
|
||||
string pattern = "(?<rpl>\nProfile\\s\"" + Regex.Escape(profileName) + "\".*?EndProfile.*?\n)";
|
||||
tmpFileContent = Regex.Replace(tmpFileContent, pattern, "", RegexOptions.Singleline);
|
||||
File.WriteAllText(tmpFileName, tmpFileContent);
|
||||
});
|
||||
|
||||
if (tmpFileContent != "")
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
LoadSettingsFileEx(hSession, tmpFileName);
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tmpFileName))
|
||||
File.Delete(tmpFileName);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteProfile(string profileName)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
if (hProfile != IntPtr.Zero)
|
||||
{
|
||||
var nvRes = nvw.DRS_DeleteProfile(hSession, hProfile);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_DeleteProfile", nvRes);
|
||||
|
||||
SaveSettings(hSession);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public List<string> GetProfileNames(ref string baseProfileName, bool enumProfiles = true)
|
||||
{
|
||||
var lstResult = new List<string>();
|
||||
var tmpBaseProfileName = baseProfileName;
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hBase = GetProfileHandle(hSession, null);
|
||||
var baseProfile = GetProfileInfo(hSession, hBase);
|
||||
tmpBaseProfileName = baseProfile.profileName;
|
||||
|
||||
lstResult.Add("_GLOBAL_DRIVER_PROFILE (" + tmpBaseProfileName + ")");
|
||||
|
||||
if (enumProfiles)
|
||||
{
|
||||
var profileHandles = EnumProfileHandles(hSession);
|
||||
foreach (IntPtr hProfile in profileHandles)
|
||||
{
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
|
||||
if (profile.isPredefined == 0 || profile.numOfApps > 0)
|
||||
{
|
||||
lstResult.Add(profile.profileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
baseProfileName = tmpBaseProfileName;
|
||||
return lstResult;
|
||||
}
|
||||
|
||||
public void CreateProfile(string profileName, string applicationName = null)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = CreateProfile(hSession, profileName);
|
||||
|
||||
if (applicationName != null)
|
||||
AddApplication(hSession, hProfile, applicationName);
|
||||
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
|
||||
public void ResetAllProfilesInternal()
|
||||
{
|
||||
RunDrsInitProcess();
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var nvRes = nvw.DRS_RestoreAllDefaults(hSession);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_RestoreAllDefaults", nvRes);
|
||||
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
|
||||
public void ResetProfile(string profileName, out bool removeFromModified)
|
||||
{
|
||||
bool tmpRemoveFromModified = false;
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
|
||||
if (profile.isPredefined == 1)
|
||||
{
|
||||
var nvRes = nvw.DRS_RestoreProfileDefault(hSession, hProfile);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_RestoreProfileDefault", nvRes);
|
||||
|
||||
SaveSettings(hSession);
|
||||
tmpRemoveFromModified = true;
|
||||
}
|
||||
else if (profile.numOfSettings > 0)
|
||||
{
|
||||
int dropCount = 0;
|
||||
var settings = GetProfileSettings(hSession, hProfile);
|
||||
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
if (setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
{
|
||||
if (nvw.DRS_DeleteProfileSetting(hSession, hProfile, setting.settingId) == NvAPI_Status.NVAPI_OK)
|
||||
{
|
||||
dropCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropCount > 0)
|
||||
{
|
||||
SaveSettings(hSession);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
removeFromModified = tmpRemoveFromModified;
|
||||
}
|
||||
|
||||
public void ResetValue(string profileName, uint settingId, out bool removeFromModified)
|
||||
{
|
||||
var tmpRemoveFromModified = false;
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
|
||||
if (hProfile != IntPtr.Zero)
|
||||
{
|
||||
var nvRes = nvw.DRS_RestoreProfileDefaultSetting(hSession, hProfile, settingId);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_RestoreProfileDefaultSetting", nvRes);
|
||||
|
||||
SaveSettings(hSession);
|
||||
|
||||
var modifyCount = 0;
|
||||
var settings = GetProfileSettings(hSession, hProfile);
|
||||
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
if (setting.isCurrentPredefined == 0 && setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
{
|
||||
modifyCount++;
|
||||
}
|
||||
}
|
||||
tmpRemoveFromModified = (modifyCount == 0);
|
||||
}
|
||||
});
|
||||
|
||||
removeFromModified = tmpRemoveFromModified;
|
||||
}
|
||||
|
||||
public uint GetDwordValueFromProfile(string profileName, uint settingId, bool returnDefaultValue = false, bool forceDedicatedScope = false)
|
||||
{
|
||||
return DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
|
||||
var dwordValue = ReadDwordValue(hSession, hProfile, settingId);
|
||||
|
||||
if (dwordValue != null)
|
||||
return dwordValue.Value;
|
||||
else if (returnDefaultValue)
|
||||
return meta.GetSettingMeta(settingId).DefaultDwordValue;
|
||||
|
||||
throw new NvapiException("DRS_GetSetting", NvAPI_Status.NVAPI_SETTING_NOT_FOUND);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetDwordValueToProfile(string profileName, uint settingId, uint dwordValue)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
StoreDwordValue(hSession, hProfile, settingId, dwordValue);
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
|
||||
public int StoreSettingsToProfile(string profileName, List<KeyValuePair<uint, string>> settings)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var settingMeta = meta.GetSettingMeta(setting.Key);
|
||||
var settingType = settingMeta.SettingType;
|
||||
|
||||
if (settingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE)
|
||||
{
|
||||
var dword = DrsUtil.ParseDwordSettingValue(settingMeta, setting.Value);
|
||||
StoreDwordValue(hSession, hProfile, setting.Key, dword);
|
||||
}
|
||||
else if (settingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE)
|
||||
{
|
||||
var str = DrsUtil.ParseStringSettingValue(settingMeta, setting.Value);
|
||||
StoreStringValue(hSession, hProfile, setting.Key, str);
|
||||
}
|
||||
else if (settingType == NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE)
|
||||
{
|
||||
var bin = DrsUtil.ParseBinarySettingValue(settingMeta, setting.Value);
|
||||
StoreBinaryValue(hSession, hProfile, setting.Key, bin);
|
||||
}
|
||||
}
|
||||
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
private SettingItem CreateSettingItem(NVDRS_SETTING setting, bool useDefault = false)
|
||||
{
|
||||
var settingMeta = meta.GetSettingMeta(setting.settingId);
|
||||
//settingMeta.SettingType = setting.settingType;
|
||||
|
||||
if (settingMeta.DwordValues == null)
|
||||
settingMeta.DwordValues = new List<SettingValue<uint>>();
|
||||
|
||||
|
||||
if (settingMeta.StringValues == null)
|
||||
settingMeta.StringValues = new List<SettingValue<string>>();
|
||||
|
||||
if (settingMeta.BinaryValues == null)
|
||||
settingMeta.BinaryValues = new List<SettingValue<byte[]>>();
|
||||
|
||||
|
||||
var settingState = SettingState.NotAssiged;
|
||||
string valueRaw = "";
|
||||
string valueText = "";
|
||||
|
||||
if (settingMeta.SettingType == NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE)
|
||||
{
|
||||
if (useDefault)
|
||||
{
|
||||
valueRaw = DrsUtil.GetDwordString(settingMeta.DefaultDwordValue);
|
||||
valueText = DrsUtil.GetDwordSettingValueName(settingMeta, settingMeta.DefaultDwordValue);
|
||||
}
|
||||
else if (setting.isCurrentPredefined == 1 && setting.isPredefinedValid == 1)
|
||||
{
|
||||
valueRaw = DrsUtil.GetDwordString(setting.predefinedValue.dwordValue);
|
||||
valueText = DrsUtil.GetDwordSettingValueName(settingMeta, setting.predefinedValue.dwordValue);
|
||||
|
||||
if (setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
settingState = SettingState.NvidiaSetting;
|
||||
else
|
||||
settingState = SettingState.GlobalSetting;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueRaw = DrsUtil.GetDwordString(setting.currentValue.dwordValue);
|
||||
valueText = DrsUtil.GetDwordSettingValueName(settingMeta, setting.currentValue.dwordValue);
|
||||
|
||||
if (setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
settingState = SettingState.UserdefinedSetting;
|
||||
else
|
||||
settingState = SettingState.GlobalSetting;
|
||||
}
|
||||
}
|
||||
|
||||
if (settingMeta.SettingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE)
|
||||
{
|
||||
if (useDefault)
|
||||
{
|
||||
valueRaw = settingMeta.DefaultStringValue;
|
||||
valueText = DrsUtil.GetStringSettingValueName(settingMeta, settingMeta.DefaultStringValue);
|
||||
}
|
||||
else if (setting.isCurrentPredefined == 1 && setting.isPredefinedValid == 1)
|
||||
{
|
||||
valueRaw = setting.predefinedValue.stringValue;
|
||||
valueText = DrsUtil.GetStringSettingValueName(settingMeta, setting.predefinedValue.stringValue);
|
||||
settingState = SettingState.NvidiaSetting;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueRaw = setting.currentValue.stringValue;
|
||||
valueText = DrsUtil.GetStringSettingValueName(settingMeta, setting.currentValue.stringValue);
|
||||
|
||||
if (setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
settingState = SettingState.UserdefinedSetting;
|
||||
else
|
||||
settingState = SettingState.GlobalSetting;
|
||||
}
|
||||
}
|
||||
|
||||
if (settingMeta.SettingType == NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE)
|
||||
{
|
||||
if (useDefault)
|
||||
{
|
||||
valueRaw = DrsUtil.GetBinaryString(settingMeta.DefaultBinaryValue);
|
||||
valueText = DrsUtil.GetBinarySettingValueName(settingMeta, settingMeta.DefaultBinaryValue);
|
||||
}
|
||||
else if (setting.isCurrentPredefined == 1 && setting.isPredefinedValid == 1)
|
||||
{
|
||||
valueRaw = DrsUtil.GetBinaryString(setting.predefinedValue.binaryValue);
|
||||
valueText = DrsUtil.GetBinarySettingValueName(settingMeta, setting.predefinedValue.binaryValue);
|
||||
settingState = SettingState.NvidiaSetting;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueRaw = DrsUtil.GetBinaryString(setting.currentValue.binaryValue);
|
||||
valueText = DrsUtil.GetBinarySettingValueName(settingMeta, setting.currentValue.binaryValue);
|
||||
|
||||
if (setting.settingLocation == NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION)
|
||||
settingState = SettingState.UserdefinedSetting;
|
||||
else
|
||||
settingState = SettingState.GlobalSetting;
|
||||
}
|
||||
}
|
||||
|
||||
return new SettingItem()
|
||||
{
|
||||
SettingId = setting.settingId,
|
||||
SettingText = settingMeta.SettingName,
|
||||
GroupName = settingMeta.GroupName,
|
||||
ValueRaw = valueRaw,
|
||||
ValueText = valueText,
|
||||
State = settingState,
|
||||
IsStringValue = settingMeta.SettingType == NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE,
|
||||
IsApiExposed = settingMeta.IsApiExposed,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public List<SettingItem> GetSettingsForProfile(string profileName, SettingViewMode viewMode, ref Dictionary<string, string> applications)
|
||||
{
|
||||
var result = new List<SettingItem>();
|
||||
var settingIds = meta.GetSettingIds(viewMode);
|
||||
settingIds.AddRange(_baseProfileSettingIds);
|
||||
settingIds = settingIds.Distinct().ToList();
|
||||
|
||||
applications = DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
|
||||
var profileSettings = GetProfileSettings(hSession, hProfile);
|
||||
foreach (var profileSetting in profileSettings)
|
||||
{
|
||||
result.Add(CreateSettingItem(profileSetting));
|
||||
|
||||
if (settingIds.Contains(profileSetting.settingId))
|
||||
settingIds.Remove(profileSetting.settingId);
|
||||
}
|
||||
|
||||
foreach (var settingId in settingIds)
|
||||
{
|
||||
var setting = ReadSetting(hSession, hProfile, settingId);
|
||||
if (setting != null)
|
||||
result.Add(CreateSettingItem(setting.Value));
|
||||
else
|
||||
{
|
||||
var dummySetting = new NVDRS_SETTING() { settingId = settingId };
|
||||
result.Add(CreateSettingItem(dummySetting, true));
|
||||
}
|
||||
}
|
||||
|
||||
return GetProfileApplications(hSession, hProfile)
|
||||
.Select(x => Tuple.Create(x.appName, GetApplicationFingerprint(x))).ToDictionary(x => x.Item2, x => x.Item1);
|
||||
|
||||
});
|
||||
|
||||
return result.OrderBy(x => x.SettingText).ThenBy(x => x.GroupName).ToList();
|
||||
}
|
||||
|
||||
public void AddApplication(string profileName, string applicationName)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
AddApplication(hSession, hProfile, applicationName);
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
|
||||
public void RemoveApplication(string profileName, string applicationFingerprint)
|
||||
{
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
var hProfile = GetProfileHandle(hSession, profileName);
|
||||
var applications = GetProfileApplications(hSession, hProfile);
|
||||
foreach (var app in applications)
|
||||
{
|
||||
if (GetApplicationFingerprint(app) != applicationFingerprint) continue;
|
||||
DeleteApplication(hSession, hProfile, app);
|
||||
break;
|
||||
}
|
||||
SaveSettings(hSession);
|
||||
});
|
||||
}
|
||||
|
||||
private string GetApplicationFingerprint(NVDRS_APPLICATION_V3 application)
|
||||
{
|
||||
return $"{application.appName}|{application.fileInFolder}|{application.userFriendlyName}|{application.launcher}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Text;
|
||||
using nvw = nspector.Native.NVAPI2.NvapiDrsWrapper;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public abstract class DrsSettingsServiceBase
|
||||
{
|
||||
|
||||
protected DrsSettingsMetaService meta;
|
||||
protected DrsDecrypterService decrypter;
|
||||
|
||||
public DrsSettingsServiceBase(DrsSettingsMetaService metaService, DrsDecrypterService decrpterService = null)
|
||||
{
|
||||
meta = metaService;
|
||||
decrypter = decrpterService;
|
||||
DriverVersion = GetDriverVersionInternal();
|
||||
}
|
||||
|
||||
public readonly float DriverVersion;
|
||||
private float GetDriverVersionInternal()
|
||||
{
|
||||
float result = 0f;
|
||||
uint sysDrvVersion = 0;
|
||||
var sysDrvBranch = new StringBuilder((int)NvapiDrsWrapper.NVAPI_SHORT_STRING_MAX);
|
||||
|
||||
if (nvw.SYS_GetDriverAndBranchVersion(ref sysDrvVersion, sysDrvBranch) == NvAPI_Status.NVAPI_OK)
|
||||
{
|
||||
try { result = (float)(sysDrvVersion / 100f); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void DrsSession(Action<IntPtr> action, bool forceNonGlobalSession = false, bool preventLoadSettings = false)
|
||||
{
|
||||
DrsSessionScope.DrsSession<bool>((hSession) =>
|
||||
{
|
||||
action(hSession);
|
||||
return true;
|
||||
}, forceNonGlobalSession: forceNonGlobalSession, preventLoadSettings: preventLoadSettings);
|
||||
}
|
||||
|
||||
protected T DrsSession<T>(Func<IntPtr, T> action, bool forceDedicatedScope = false)
|
||||
{
|
||||
return DrsSessionScope.DrsSession<T>(action, forceDedicatedScope);
|
||||
}
|
||||
|
||||
protected IntPtr GetProfileHandle(IntPtr hSession, string profileName)
|
||||
{
|
||||
var hProfile = IntPtr.Zero;
|
||||
|
||||
if (string.IsNullOrEmpty(profileName))
|
||||
{
|
||||
var nvRes = nvw.DRS_GetCurrentGlobalProfile(hSession, ref hProfile);
|
||||
|
||||
if (hProfile == IntPtr.Zero)
|
||||
throw new NvapiException("DRS_GetCurrentGlobalProfile", NvAPI_Status.NVAPI_PROFILE_NOT_FOUND);
|
||||
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_GetCurrentGlobalProfile", nvRes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nvRes = nvw.DRS_FindProfileByName(hSession, new StringBuilder(profileName), ref hProfile);
|
||||
|
||||
if (nvRes == NvAPI_Status.NVAPI_PROFILE_NOT_FOUND)
|
||||
return IntPtr.Zero;
|
||||
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_FindProfileByName", nvRes);
|
||||
}
|
||||
return hProfile;
|
||||
}
|
||||
|
||||
protected IntPtr CreateProfile(IntPtr hSession, string profileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(profileName))
|
||||
throw new ArgumentNullException("profileName");
|
||||
|
||||
var hProfile = IntPtr.Zero;
|
||||
|
||||
var newProfile = new NVDRS_PROFILE()
|
||||
{
|
||||
version = nvw.NVDRS_PROFILE_VER,
|
||||
profileName = profileName,
|
||||
};
|
||||
|
||||
var nvRes = nvw.DRS_CreateProfile(hSession, ref newProfile, ref hProfile);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_CreateProfile", nvRes);
|
||||
|
||||
return hProfile;
|
||||
}
|
||||
|
||||
|
||||
protected NVDRS_PROFILE GetProfileInfo(IntPtr hSession, IntPtr hProfile)
|
||||
{
|
||||
var tmpProfile = new NVDRS_PROFILE();
|
||||
tmpProfile.version = nvw.NVDRS_PROFILE_VER;
|
||||
|
||||
var gpRes = nvw.DRS_GetProfileInfo(hSession, hProfile, ref tmpProfile);
|
||||
if (gpRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_GetProfileInfo", gpRes);
|
||||
|
||||
return tmpProfile;
|
||||
}
|
||||
|
||||
protected void StoreSetting(IntPtr hSession, IntPtr hProfile, NVDRS_SETTING newSetting)
|
||||
{
|
||||
var ssRes = nvw.DRS_SetSetting(hSession, hProfile, ref newSetting);
|
||||
if (ssRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SetSetting", ssRes);
|
||||
}
|
||||
|
||||
protected void StoreDwordValue(IntPtr hSession, IntPtr hProfile, uint settingId, uint dwordValue)
|
||||
{
|
||||
var newSetting = new NVDRS_SETTING()
|
||||
{
|
||||
version = nvw.NVDRS_SETTING_VER,
|
||||
settingId = settingId,
|
||||
settingType = NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE,
|
||||
settingLocation = NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION,
|
||||
currentValue = new NVDRS_SETTING_UNION()
|
||||
{
|
||||
dwordValue = dwordValue,
|
||||
},
|
||||
};
|
||||
|
||||
var ssRes = nvw.DRS_SetSetting(hSession, hProfile, ref newSetting);
|
||||
if (ssRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SetSetting", ssRes);
|
||||
|
||||
}
|
||||
|
||||
protected void StoreStringValue(IntPtr hSession, IntPtr hProfile, uint settingId, string stringValue)
|
||||
{
|
||||
var newSetting = new NVDRS_SETTING()
|
||||
{
|
||||
version = nvw.NVDRS_SETTING_VER,
|
||||
settingId = settingId,
|
||||
settingType = NVDRS_SETTING_TYPE.NVDRS_WSTRING_TYPE,
|
||||
settingLocation = NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION,
|
||||
currentValue = new NVDRS_SETTING_UNION()
|
||||
{
|
||||
stringValue = stringValue,
|
||||
},
|
||||
};
|
||||
|
||||
var ssRes = nvw.DRS_SetSetting(hSession, hProfile, ref newSetting);
|
||||
if (ssRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SetSetting", ssRes);
|
||||
|
||||
}
|
||||
|
||||
protected void StoreBinaryValue(IntPtr hSession, IntPtr hProfile, uint settingId, byte[] binValue)
|
||||
{
|
||||
var newSetting = new NVDRS_SETTING()
|
||||
{
|
||||
version = nvw.NVDRS_SETTING_VER,
|
||||
settingId = settingId,
|
||||
settingType = NVDRS_SETTING_TYPE.NVDRS_BINARY_TYPE,
|
||||
settingLocation = NVDRS_SETTING_LOCATION.NVDRS_CURRENT_PROFILE_LOCATION,
|
||||
currentValue = new NVDRS_SETTING_UNION()
|
||||
{
|
||||
binaryValue = binValue,
|
||||
},
|
||||
};
|
||||
|
||||
var ssRes = nvw.DRS_SetSetting(hSession, hProfile, ref newSetting);
|
||||
if (ssRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SetSetting", ssRes);
|
||||
|
||||
}
|
||||
|
||||
protected NVDRS_SETTING? ReadSetting(IntPtr hSession, IntPtr hProfile, uint settingId)
|
||||
{
|
||||
var newSetting = new NVDRS_SETTING()
|
||||
{
|
||||
version = nvw.NVDRS_SETTING_VER,
|
||||
};
|
||||
|
||||
var ssRes = nvw.DRS_GetSetting(hSession, hProfile, settingId, ref newSetting);
|
||||
if (ssRes == NvAPI_Status.NVAPI_SETTING_NOT_FOUND)
|
||||
return null;
|
||||
|
||||
if (ssRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_GetSetting", ssRes);
|
||||
|
||||
if (decrypter != null)
|
||||
{
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
decrypter.DecryptSettingIfNeeded(profile.profileName, ref newSetting);
|
||||
}
|
||||
|
||||
return newSetting;
|
||||
}
|
||||
|
||||
protected uint? ReadDwordValue(IntPtr hSession, IntPtr hProfile, uint settingId)
|
||||
{
|
||||
var newSetting = ReadSetting(hSession, hProfile, settingId);
|
||||
if (newSetting == null)
|
||||
return null;
|
||||
return newSetting.Value.currentValue.dwordValue;
|
||||
}
|
||||
|
||||
protected void AddApplication(IntPtr hSession, IntPtr hProfile, string applicationName)
|
||||
{
|
||||
var newApp = new NVDRS_APPLICATION_V3()
|
||||
{
|
||||
version = nvw.NVDRS_APPLICATION_VER_V3,
|
||||
appName = applicationName,
|
||||
};
|
||||
|
||||
var caRes = nvw.DRS_CreateApplication(hSession, hProfile, ref newApp);
|
||||
if (caRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_CreateApplication", caRes);
|
||||
|
||||
}
|
||||
|
||||
protected void DeleteApplication(IntPtr hSession, IntPtr hProfile, NVDRS_APPLICATION_V3 application)
|
||||
{
|
||||
var caRes = nvw.DRS_DeleteApplicationEx(hSession, hProfile, ref application);
|
||||
if (caRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_DeleteApplication", caRes);
|
||||
}
|
||||
|
||||
protected List<IntPtr> EnumProfileHandles(IntPtr hSession)
|
||||
{
|
||||
var profileHandles = new List<IntPtr>();
|
||||
var hProfile = IntPtr.Zero;
|
||||
uint index = 0;
|
||||
|
||||
NvAPI_Status nvRes;
|
||||
|
||||
do
|
||||
{
|
||||
nvRes = nvw.DRS_EnumProfiles(hSession, index, ref hProfile);
|
||||
if (nvRes == NvAPI_Status.NVAPI_OK)
|
||||
{
|
||||
profileHandles.Add(hProfile);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
while (nvRes == NvAPI_Status.NVAPI_OK);
|
||||
|
||||
if (nvRes != NvAPI_Status.NVAPI_END_ENUMERATION)
|
||||
throw new NvapiException("DRS_EnumProfiles", nvRes);
|
||||
|
||||
return profileHandles;
|
||||
}
|
||||
|
||||
protected List<NVDRS_SETTING> GetProfileSettings(IntPtr hSession, IntPtr hProfile)
|
||||
{
|
||||
uint settingCount = 512;
|
||||
var settings = new NVDRS_SETTING[settingCount];
|
||||
settings[0].version = NvapiDrsWrapper.NVDRS_SETTING_VER;
|
||||
|
||||
var esRes = NvapiDrsWrapper.DRS_EnumSettings(hSession, hProfile, 0, ref settingCount, ref settings);
|
||||
|
||||
if (esRes == NvAPI_Status.NVAPI_END_ENUMERATION)
|
||||
return new List<NVDRS_SETTING>();
|
||||
|
||||
if (esRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_EnumSettings", esRes);
|
||||
|
||||
if (decrypter != null)
|
||||
{
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
for (int i = 0; i < settingCount; i++)
|
||||
{
|
||||
decrypter.DecryptSettingIfNeeded(profile.profileName, ref settings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return settings.ToList();
|
||||
}
|
||||
|
||||
protected List<NVDRS_APPLICATION_V3> GetProfileApplications(IntPtr hSession, IntPtr hProfile)
|
||||
{
|
||||
uint appCount = 512;
|
||||
var apps = new NVDRS_APPLICATION_V3[512];
|
||||
apps[0].version = NvapiDrsWrapper.NVDRS_APPLICATION_VER_V3;
|
||||
|
||||
var esRes = NvapiDrsWrapper.DRS_EnumApplications(hSession, hProfile, 0, ref appCount, ref apps);
|
||||
|
||||
if (esRes == NvAPI_Status.NVAPI_END_ENUMERATION)
|
||||
return new List<NVDRS_APPLICATION_V3>();
|
||||
|
||||
if (esRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_EnumApplications", esRes);
|
||||
|
||||
return apps.ToList();
|
||||
}
|
||||
|
||||
protected void SaveSettings(IntPtr hSession)
|
||||
{
|
||||
var nvRes = nvw.DRS_SaveSettings(hSession);
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SaveSettings", nvRes);
|
||||
}
|
||||
|
||||
protected void LoadSettingsFileEx(IntPtr hSession, string filename)
|
||||
{
|
||||
var nvRes = nvw.DRS_LoadSettingsFromFileEx(hSession, new StringBuilder(filename));
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_LoadSettingsFromFileEx", nvRes);
|
||||
}
|
||||
|
||||
protected void SaveSettingsFileEx(IntPtr hSession, string filename)
|
||||
{
|
||||
var nvRes = nvw.DRS_SaveSettingsToFileEx(hSession, new StringBuilder(filename));
|
||||
if (nvRes != NvAPI_Status.NVAPI_OK)
|
||||
throw new NvapiException("DRS_SaveSettingsToFileEx", nvRes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
using nspector.Common.Meta;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace nspector.Common
|
||||
{
|
||||
public static class DrsUtil
|
||||
{
|
||||
public static string StringValueRaw = "Text";
|
||||
|
||||
public static string GetDwordString(uint dword)
|
||||
{
|
||||
return string.Format("0x{0:X8}", dword);
|
||||
}
|
||||
|
||||
public static uint ParseDwordByInputSafe(string input)
|
||||
{
|
||||
uint result = 0;
|
||||
if (input.ToLower().StartsWith("0x"))
|
||||
{
|
||||
try
|
||||
{
|
||||
int blankPos = input.IndexOf(' ');
|
||||
int parseLen = blankPos > 2 ? blankPos - 2 : input.Length - 2;
|
||||
result = uint.Parse(input.Substring(2, parseLen), NumberStyles.AllowHexSpecifier);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
try { result = uint.Parse(input); }
|
||||
catch { }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static uint ParseDwordSettingValue(SettingMeta meta, string text)
|
||||
{
|
||||
var valueByName = meta.DwordValues.FirstOrDefault(x => x.ValueName != null && x.ValueName.Equals(text));
|
||||
if (valueByName != null)
|
||||
return valueByName.Value;
|
||||
|
||||
return ParseDwordByInputSafe(text);
|
||||
}
|
||||
|
||||
internal static string GetDwordSettingValueName(SettingMeta meta, uint dwordValue)
|
||||
{
|
||||
var settingValue = meta.DwordValues
|
||||
.FirstOrDefault(x => x.Value.Equals(dwordValue));
|
||||
|
||||
return settingValue == null ? GetDwordString(dwordValue) : settingValue.ValueName;
|
||||
}
|
||||
|
||||
internal static string ParseStringSettingValue(SettingMeta meta, string text)
|
||||
{
|
||||
var valueByName = meta.StringValues.FirstOrDefault(x => x.ValueName != null && x.ValueName.Equals(text));
|
||||
if (valueByName != null)
|
||||
return valueByName.Value;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
internal static string GetStringSettingValueName(SettingMeta meta, string stringValue)
|
||||
{
|
||||
var settingValue = meta.StringValues
|
||||
.FirstOrDefault(x => x.Value.Equals(stringValue));
|
||||
|
||||
return settingValue == null ? stringValue : settingValue.ValueName;
|
||||
}
|
||||
|
||||
public static string GetBinaryString(byte[] binaryValue)
|
||||
{
|
||||
if (binaryValue == null)
|
||||
return "";
|
||||
|
||||
if (binaryValue.Length == 8)
|
||||
return string.Format("0x{0:X16}", BitConverter.ToUInt64(binaryValue, 0));
|
||||
|
||||
return BitConverter.ToString(binaryValue);
|
||||
}
|
||||
|
||||
internal static string GetBinarySettingValueName(SettingMeta meta, byte[] binaryValue)
|
||||
{
|
||||
var settingValue = meta.BinaryValues?
|
||||
.FirstOrDefault(x => x.Value.Equals(binaryValue));
|
||||
|
||||
return settingValue == null ? GetBinaryString(binaryValue) : settingValue.ValueName;
|
||||
}
|
||||
|
||||
internal static byte[] ParseBinarySettingValue(SettingMeta meta, string text)
|
||||
{
|
||||
var valueByName = meta.BinaryValues.FirstOrDefault(x => x.ValueName != null && x.ValueName.Equals(text));
|
||||
if (valueByName != null)
|
||||
return valueByName.Value;
|
||||
|
||||
return ParseBinaryByInputSafe(text);
|
||||
}
|
||||
|
||||
public static byte[] ParseBinaryByInputSafe(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return null;
|
||||
|
||||
if (input.StartsWith("0x"))
|
||||
{
|
||||
int blankPos = input.IndexOf(' ');
|
||||
int parseLen = blankPos > 2 ? blankPos - 2 : input.Length - 2;
|
||||
var qword = ulong.Parse(input.Substring(2, parseLen), NumberStyles.AllowHexSpecifier);
|
||||
return BitConverter.GetBytes(qword);
|
||||
}
|
||||
|
||||
if (input.Contains("-"))
|
||||
return Array.ConvertAll<string, byte>(input.Split('-'), s => Convert.ToByte(s, 16));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
public static class AdminHelper
|
||||
{
|
||||
private static bool isAdmin = false;
|
||||
static AdminHelper()
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
public static bool IsAdmin
|
||||
{
|
||||
get { return isAdmin; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
|
||||
//by Bryce Wagner https://stackoverflow.com/questions/13139074/mouse-wheel-scrolling-toolstrip-menu-items
|
||||
|
||||
public class DropDownMenuScrollWheelHandler : IMessageFilter
|
||||
{
|
||||
private static DropDownMenuScrollWheelHandler Instance;
|
||||
public static void Enable(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = new DropDownMenuScrollWheelHandler();
|
||||
Application.AddMessageFilter(Instance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
Application.RemoveMessageFilter(Instance);
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
private IntPtr activeHwnd;
|
||||
private ToolStripDropDown activeMenu;
|
||||
|
||||
public bool PreFilterMessage(ref Message m)
|
||||
{
|
||||
if (m.Msg == 0x200 && activeHwnd != m.HWnd) // WM_MOUSEMOVE
|
||||
{
|
||||
activeHwnd = m.HWnd;
|
||||
this.activeMenu = Control.FromHandle(m.HWnd) as ToolStripDropDown;
|
||||
}
|
||||
else if (m.Msg == 0x20A && this.activeMenu != null) // WM_MOUSEWHEEL
|
||||
{
|
||||
int delta = (short)(ushort)(((uint)(ulong)m.WParam) >> 16);
|
||||
HandleDelta(this.activeMenu, delta);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly Action<ToolStrip, int> ScrollInternal
|
||||
= (Action<ToolStrip, int>)Delegate.CreateDelegate(typeof(Action<ToolStrip, int>),
|
||||
typeof(ToolStrip).GetMethod("ScrollInternal",
|
||||
System.Reflection.BindingFlags.NonPublic
|
||||
| System.Reflection.BindingFlags.Instance));
|
||||
|
||||
private void HandleDelta(ToolStripDropDown ts, int delta)
|
||||
{
|
||||
if (ts.Items.Count == 0)
|
||||
return;
|
||||
|
||||
var firstItem = ts.Items[0];
|
||||
var lastItem = ts.Items[ts.Items.Count - 1];
|
||||
|
||||
if (lastItem.Bounds.Bottom < ts.Height && firstItem.Bounds.Top > 0)
|
||||
return;
|
||||
|
||||
delta = delta / -4;
|
||||
|
||||
if (delta < 0 && firstItem.Bounds.Top - delta > 9)
|
||||
{
|
||||
delta = firstItem.Bounds.Top - 9;
|
||||
}
|
||||
else if (delta > 0 && delta > lastItem.Bounds.Bottom - ts.Height + 9)
|
||||
{
|
||||
delta = lastItem.Bounds.Bottom - ts.Height + 9;
|
||||
}
|
||||
|
||||
if (delta != 0)
|
||||
ScrollInternal(ts, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
internal class InputBox
|
||||
{
|
||||
internal static DialogResult Show(string title, string promptText, ref string value, List<string> invalidInputs, string mandatoryFormatRegExPattern, int maxLength)
|
||||
{
|
||||
var form = new Form();
|
||||
var label = new Label();
|
||||
var textBox = new TextBox();
|
||||
var buttonOk = new Button();
|
||||
var buttonCancel = new Button();
|
||||
var imageBox = new PictureBox();
|
||||
|
||||
EventHandler textchanged = delegate (object sender, EventArgs e)
|
||||
{
|
||||
bool mandatory_success = Regex.IsMatch(textBox.Text, mandatoryFormatRegExPattern);
|
||||
|
||||
if (textBox.Text == "" || textBox.Text.Length > maxLength || !mandatory_success)
|
||||
{
|
||||
imageBox.Image = nspector.Properties.Resources.ieframe_1_18212;
|
||||
buttonOk.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string invStr in invalidInputs)
|
||||
{
|
||||
if (textBox.Text.ToUpper() == invStr.ToUpper())
|
||||
{
|
||||
imageBox.Image = Properties.Resources.ieframe_1_18212;
|
||||
buttonOk.Enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imageBox.Image = Properties.Resources.ieframe_1_31073_002;
|
||||
buttonOk.Enabled = true;
|
||||
};
|
||||
|
||||
|
||||
textBox.TextChanged += textchanged;
|
||||
|
||||
|
||||
form.Text = title;
|
||||
label.Text = promptText;
|
||||
textBox.Text = value;
|
||||
textBox.MaxLength = maxLength;
|
||||
imageBox.Image = Properties.Resources.ieframe_1_18212;
|
||||
|
||||
buttonOk.Text = "OK";
|
||||
buttonCancel.Text = "Cancel";
|
||||
buttonOk.DialogResult = DialogResult.OK;
|
||||
buttonCancel.DialogResult = DialogResult.Cancel;
|
||||
|
||||
buttonOk.Enabled = false;
|
||||
|
||||
label.SetBounds(9, 20, 372, 13);
|
||||
textBox.SetBounds(12, 36, 352, 20);
|
||||
buttonOk.SetBounds(228, 72, 75, 23);
|
||||
buttonCancel.SetBounds(309, 72, 75, 23);
|
||||
|
||||
imageBox.SetBounds(368, 36, 16, 16);
|
||||
|
||||
label.AutoSize = true;
|
||||
imageBox.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||||
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
|
||||
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
|
||||
form.ClientSize = new Size(396, 107);
|
||||
form.Controls.AddRange(new Control[] { label, textBox, imageBox, buttonOk, buttonCancel });
|
||||
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
|
||||
form.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
form.StartPosition = FormStartPosition.CenterParent;
|
||||
form.MinimizeBox = false;
|
||||
form.MaximizeBox = false;
|
||||
form.AcceptButton = buttonOk;
|
||||
form.CancelButton = buttonCancel;
|
||||
|
||||
textchanged(form, new EventArgs());
|
||||
|
||||
DialogResult dialogResult = form.ShowDialog();
|
||||
value = textBox.Text;
|
||||
return dialogResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
internal class ListSort : IComparer
|
||||
{
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.String.CompareOrdinal(x.ToString(), y.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
|
||||
public class ListViewGroupHeaderSorter : IComparer<ListViewGroup>
|
||||
{
|
||||
private bool _ascending = true;
|
||||
public ListViewGroupHeaderSorter(bool ascending)
|
||||
{
|
||||
_ascending = ascending;
|
||||
}
|
||||
|
||||
#region IComparer<ListViewGroup> Members
|
||||
|
||||
public int Compare(ListViewGroup x, ListViewGroup y)
|
||||
{
|
||||
if (_ascending)
|
||||
return string.Compare(((ListViewGroup)x).Header, ((ListViewGroup)y).Header);
|
||||
else
|
||||
return string.Compare(((ListViewGroup)y).Header, ((ListViewGroup)x).Header);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ListViewGroupSorter
|
||||
{
|
||||
internal ListView _listview;
|
||||
|
||||
public static bool operator ==(ListView listview, ListViewGroupSorter sorter)
|
||||
{
|
||||
return listview == sorter._listview;
|
||||
}
|
||||
public static bool operator !=(ListView listview, ListViewGroupSorter sorter)
|
||||
{
|
||||
return listview != sorter._listview;
|
||||
}
|
||||
|
||||
public static implicit operator ListView(ListViewGroupSorter sorter)
|
||||
{
|
||||
return sorter._listview;
|
||||
}
|
||||
public static implicit operator ListViewGroupSorter(ListView listview)
|
||||
{
|
||||
return new ListViewGroupSorter(listview);
|
||||
}
|
||||
|
||||
internal ListViewGroupSorter(ListView listview)
|
||||
{
|
||||
_listview = listview;
|
||||
}
|
||||
|
||||
public void SortGroups(bool ascending)
|
||||
{
|
||||
_listview.BeginUpdate();
|
||||
List<ListViewGroup> lvgs = new List<ListViewGroup>();
|
||||
foreach (ListViewGroup lvg in _listview.Groups)
|
||||
lvgs.Add(lvg);
|
||||
_listview.Groups.Clear();
|
||||
lvgs.Sort(new ListViewGroupHeaderSorter(ascending));
|
||||
_listview.Groups.AddRange(lvgs.ToArray());
|
||||
_listview.EndUpdate();
|
||||
}
|
||||
|
||||
#region overridden methods
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return _listview.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _listview.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _listview.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace nspector.Common.Helper
|
||||
{
|
||||
internal class NoBorderRenderer : ToolStripProfessionalRenderer
|
||||
{
|
||||
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) {}
|
||||
|
||||
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) {}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 193 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user