add triggers, fixed some bugs

This commit is contained in:
Vincent Maas committed 2021-08-13 15:11:33 +02:00
1 parent 90ca095954
commit 92f4ab8ef5
18 files changed
+1688 -271

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@ namespace ColorControl
public bool primaryDisplay { get; set; }
public string displayName { get; set; }
public AmdPreset()
public AmdPreset() : base()
{
applyColorData = true;
applyDithering = false;
+564
View File
@@ -0,0 +1,564 @@
using NWin32;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ColorControl
{
// This class takes care of wrapping "Connecting and Configuring Displays(CCD) Win32 API"
internal static class CCD
{
public enum DisplayTopology
{
Internal,
External,
Extend,
Clone
}
public static DisplayTopology GetDisplayTopology()
{
uint numPathArrayElements;
uint numModeInfoArrayElements;
NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.DatabaseCurrent, out numPathArrayElements, out numModeInfoArrayElements);
var pathArray = new DisplayConfigPathInfo[numPathArrayElements];
var modeArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
DisplayConfigTopologyId displayTopology;
NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref numPathArrayElements, pathArray, ref numModeInfoArrayElements, modeArray, out displayTopology);
switch (displayTopology)
{
case DisplayConfigTopologyId.External: return DisplayTopology.External;
case DisplayConfigTopologyId.Internal: return DisplayTopology.Internal;
case DisplayConfigTopologyId.Extend: return DisplayTopology.Extend;
}
return DisplayTopology.Clone;
}
public static void SetDisplayTopology(DisplayTopology displayTopology)
{
switch (displayTopology)
{
case DisplayTopology.External:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExternal));
break;
case DisplayTopology.Internal:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyInternal));
break;
case DisplayTopology.Extend:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExtend));
break;
case DisplayTopology.Clone:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyClone));
break;
}
}
public static void SetHDRState(bool enabled, string displayName = null)
{
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
if (err == NativeConstants.ERROR_SUCCESS)
{
var pathsArray = new DisplayConfigPathInfo[pathCount];
var modesArray = new DisplayConfigModeInfo[modeCount];
DisplayConfigTopologyId displayTopology;
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
if (err == NativeConstants.ERROR_SUCCESS)
{
foreach (var path in pathsArray)
{
// get display name
var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
info.header.size = Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
info.header.adapterId = path.sourceInfo.adapterId;
info.header.id = path.sourceInfo.id;
err = NativeMethods.DisplayConfigGetDeviceInfo(ref info);
if (err != NativeConstants.ERROR_SUCCESS)
{
break;
}
var deviceName = info.viewGdiDeviceName;
if (displayName != null && !EqualDisplayNames(deviceName, displayName))
{
continue;
}
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>(); ;
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>(); ;
for (int i = 0; i < modeCount; i++)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
{
setpacket.header.adapterId = modesArray[i].adapterId;
setpacket.header.id = modesArray[i].id;
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS)
{
if (requestpacket.advancedColorSupported)
{
setpacket.enableAdvancedColor = enabled ? 1U : 0;
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
}
}
}
}
}
}
}
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
}
private static bool EqualDisplayNames(string displayName1, string displayName2)
{
displayName1 = displayName1.Replace("\\", string.Empty);
displayName2 = displayName2.Replace("\\", string.Empty);
return displayName1.Equals(displayName2, StringComparison.OrdinalIgnoreCase);
}
private enum DISPLAYCONFIG_MODE_INFO_TYPE
{
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
}
private enum DISPLAYCONFIG_DEVICE_INFO_TYPE
{
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
public DISPLAYCONFIG_DEVICE_INFO_TYPE type;
public int size;
public LUID adapterId;
public uint id;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
uint LowPart;
uint HighPart;
}
[Flags]
enum DisplayConfigVideoOutputTechnology : uint
{
Other = 4294967295, // -1
Hd15 = 0,
Svideo = 1,
CompositeVideo = 2,
ComponentVideo = 3,
Dvi = 4,
Hdmi = 5,
Lvds = 6,
DJpn = 8,
Sdi = 9,
DisplayportExternal = 10,
DisplayportEmbedded = 11,
UdiExternal = 12,
UdiEmbedded = 13,
Sdtvdongle = 14,
Internal = 0x80000000,
ForceUint32 = 0xFFFFFFFF
}
#region SdcFlags enum
[Flags]
enum SdcFlags : uint
{
Zero = 0,
TopologyInternal = 0x00000001,
TopologyClone = 0x00000002,
TopologyExtend = 0x00000004,
TopologyExternal = 0x00000008,
TopologySupplied = 0x00000010,
UseSuppliedDisplayConfig = 0x00000020,
Validate = 0x00000040,
Apply = 0x00000080,
NoOptimization = 0x00000100,
SaveToDatabase = 0x00000200,
AllowChanges = 0x00000400,
PathPersistIfRequired = 0x00000800,
ForceModeEnumeration = 0x00001000,
AllowPathOrderChanges = 0x00002000,
UseDatabaseCurrent = TopologyInternal | TopologyClone | TopologyExtend | TopologyExternal
}
[Flags]
enum DisplayConfigFlags : uint
{
Zero = 0x0,
PathActive = 0x00000001
}
[Flags]
enum DisplayConfigSourceStatus
{
Zero = 0x0,
InUse = 0x00000001
}
[Flags]
enum DisplayConfigTargetStatus : uint
{
Zero = 0x0,
InUse = 0x00000001,
FORCIBLE = 0x00000002,
ForcedAvailabilityBoot = 0x00000004,
ForcedAvailabilityPath = 0x00000008,
ForcedAvailabilitySystem = 0x00000010,
}
[Flags]
enum DisplayConfigRotation : uint
{
Zero = 0x0,
Identity = 1,
Rotate90 = 2,
Rotate180 = 3,
Rotate270 = 4,
ForceUint32 = 0xFFFFFFFF
}
[Flags]
enum DisplayConfigPixelFormat : uint
{
Zero = 0x0,
Pixelformat8Bpp = 1,
Pixelformat16Bpp = 2,
Pixelformat24Bpp = 3,
Pixelformat32Bpp = 4,
PixelformatNongdi = 5,
PixelformatForceUint32 = 0xffffffff
}
[Flags]
enum DisplayConfigScaling : uint
{
Zero = 0x0,
Identity = 1,
Centered = 2,
Stretched = 3,
Aspectratiocenteredmax = 4,
Custom = 5,
Preferred = 128,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigRational
{
uint numerator;
uint denominator;
}
[Flags]
enum DisplayConfigScanLineOrdering : uint
{
Unspecified = 0,
Progressive = 1,
Interlaced = 2,
InterlacedUpperfieldfirst = Interlaced,
InterlacedLowerfieldfirst = 3,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathInfo
{
public DisplayConfigPathSourceInfo sourceInfo;
public DisplayConfigPathTargetInfo targetInfo;
public uint flags;
}
[Flags]
enum DisplayConfigModeInfoType : uint
{
Zero = 0,
Source = 1,
Target = 2,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Explicit)]
struct DisplayConfigModeInfo
{
[FieldOffset(0)]
public DisplayConfigModeInfoType infoType;
[FieldOffset(4)]
public uint id;
[FieldOffset(8)]
public LUID adapterId;
[FieldOffset(16)]
public DisplayConfigTargetMode targetMode;
[FieldOffset(16)]
public DisplayConfigSourceMode sourceMode;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfig2DRegion
{
uint cx;
uint cy;
}
[Flags]
enum D3DmdtVideoSignalStandard : uint
{
Uninitialized = 0,
VesaDmt = 1,
VesaGtf = 2,
VesaCvt = 3,
Ibm = 4,
Apple = 5,
NtscM = 6,
NtscJ = 7,
Ntsc443 = 8,
PalB = 9,
PalB1 = 10,
PalG = 11,
PalH = 12,
PalI = 13,
PalD = 14,
PalN = 15,
PalNc = 16,
SecamB = 17,
SecamD = 18,
SecamG = 19,
SecamH = 20,
SecamK = 21,
SecamK1 = 22,
SecamL = 23,
SecamL1 = 24,
Eia861 = 25,
Eia861A = 26,
Eia861B = 27,
PalK = 28,
PalK1 = 29,
PalL = 30,
PalM = 31,
Other = 255
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigVideoSignalInfo
{
long pixelRate;
DisplayConfigRational hSyncFreq;
DisplayConfigRational vSyncFreq;
DisplayConfig2DRegion activeSize;
DisplayConfig2DRegion totalSize;
D3DmdtVideoSignalStandard videoStandard;
DisplayConfigScanLineOrdering ScanLineOrdering;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigTargetMode
{
DisplayConfigVideoSignalInfo targetVideoSignalInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct PointL
{
int x;
int y;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigSourceMode
{
uint width;
uint height;
DisplayConfigPixelFormat pixelFormat;
PointL position;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathSourceInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigSourceStatus statusFlags;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathTargetInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigVideoOutputTechnology outputTechnology;
public DisplayConfigRotation rotation;
public DisplayConfigScaling scaling;
public DisplayConfigRational refreshRate;
public DisplayConfigScanLineOrdering scanLineOrdering;
public bool targetAvailable;
public DisplayConfigTargetStatus statusFlags;
}
[Flags]
enum QueryDisplayFlags : uint
{
Zero = 0x0,
AllPaths = 0x00000001,
OnlyActivePaths = 0x00000002,
DatabaseCurrent = 0x00000004
}
[Flags]
enum DisplayConfigTopologyId : uint
{
Zero = 0x0,
Internal = 0x00000001,
Clone = 0x00000002,
Extend = 0x00000004,
External = 0x00000008,
ForceUint32 = 0xFFFFFFFF
}
private enum DISPLAYCONFIG_COLOR_ENCODING
{
DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3,
DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint value;
public DISPLAYCONFIG_COLOR_ENCODING colorEncoding;
public int bitsPerColorChannel;
public bool advancedColorSupported => (value & 0x1) == 0x1;
public bool advancedColorEnabled => (value & 0x2) == 0x2;
public bool wideColorEnforced => (value & 0x4) == 0x4;
public bool advancedColorForceDisabled => (value & 0x8) == 0x8;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint enableAdvancedColor;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
{
public uint value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
public DisplayConfigVideoOutputTechnology outputTechnology;
public ushort edidManufactureId;
public ushort edidProductCodeId;
public uint connectorInstance;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string monitorFriendlyDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string monitorDevicePat;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string viewGdiDeviceName;
}
#endregion
static class NativeMethods
{
[DllImport("User32.dll")]
public static extern int GetDisplayConfigBufferSizes(QueryDisplayFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
[DllImport("User32.dll")]
public static extern int SetDisplayConfig(uint numPathArrayElements, [In] DisplayConfigPathInfo[] pathArray, uint numModeInfoArrayElements, [In] DisplayConfigModeInfo[] modeInfoArray, SdcFlags flags);
[DllImport("User32.dll")]
public static extern int QueryDisplayConfig(QueryDisplayFlags flags, ref uint numPathArrayElements, [In, Out] DisplayConfigPathInfo[] pathInfoArray, ref uint modeInfoArrayElements, [In, Out] DisplayConfigModeInfo[] modeInfoArray, out DisplayConfigTopologyId id);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO setPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);
}
}
}
+12 -10
View File
@@ -27,7 +27,7 @@
<ProductName>ColorControl</ProductName>
<PublisherName>Maassoft</PublisherName>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>3.3.0.0</ApplicationVersion>
<ApplicationVersion>4.0.0.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
@@ -74,10 +74,10 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>482C987E8C69872608C9D44751E004C5784E8540</ManifestCertificateThumbprint>
<ManifestCertificateThumbprint>CF0BC153EA664CD97AC6F234B91F45D34630958F</ManifestCertificateThumbprint>
</PropertyGroup>
<PropertyGroup>
<ManifestKeyFile>ColorControl_TemporaryKey.pfx</ManifestKeyFile>
<ManifestKeyFile>ColorControl_1_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<PropertyGroup>
<GenerateManifests>true</GenerateManifests>
@@ -104,23 +104,23 @@
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.9.1.0, Culture=neutral, PublicKeyToken=e25603a88b3aa7da, processorArchitecture=MSIL">
<HintPath>..\packages\TaskScheduler.2.9.1\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
</Reference>
<Reference Include="Native, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Native.0.1.0\lib\net35\Native.dll</HintPath>
<Reference 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.9\lib\net45\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.7.10\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="NStandard, Version=0.6.12.5, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NStandard.0.6.12.5\lib\net46\NStandard.dll</HintPath>
<Reference 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.0.7.6, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NWin32.1.0.7.6\lib\net46\NWin32.dll</HintPath>
<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>
@@ -177,6 +177,7 @@
<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>
@@ -239,6 +240,7 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Resources\RC_coords.txt" />
<None Include="ColorControl_1_TemporaryKey.pfx" />
<None Include="Resources\LG_remote.jpg" />
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
+106 -34
View File
@@ -13,7 +13,7 @@ namespace ColorControl
{
public class InvokableAction
{
public Func<Dictionary<string, string>, bool> Function { get; set; }
public Func<Dictionary<string, object>, bool> Function { get; set; }
public string Name { get; set; }
public Type EnumType { get; set; }
public decimal MinValue { get; set; }
@@ -21,6 +21,12 @@ namespace ColorControl
public string Category { get; set; }
}
public class LgDevicePictureSettings
{
public int Backlight { get; set; }
public int Contrast { get; set; }
}
public enum PowerState
{
Unknown,
@@ -69,20 +75,27 @@ namespace ColorControl
private DateTimeOffset _poweredOffViaAppDateTime { get; set; }
[JsonIgnore]
private List<InvokableAction> _invokableActions = new List<InvokableAction>();
[JsonIgnore]
private SemaphoreSlim _connectSemaphore = new SemaphoreSlim(1, 1);
[JsonIgnore]
public string ModelName { get; private set; }
[JsonIgnore]
public LgDevicePictureSettings PictureSettings { get; private set; }
[JsonConstructor]
public LgDevice(string name, string ipAddress, string macAddress, bool isCustom = true, bool isDummy = false)
{
PictureSettings = new LgDevicePictureSettings();
Name = name;
IpAddress = ipAddress;
MacAddress = macAddress;
IsCustom = isCustom;
IsDummy = isDummy;
AddInvokableAction("WOL", new Func<Dictionary<string, string>, bool>(WakeAction));
AddInvokableAction("WOL", new Func<Dictionary<string, object>, bool>(WakeAction));
AddGenericPictureAction("backlight", minValue: 0, maxValue: 100);
AddGenericPictureAction("brightness", minValue: 0, maxValue: 100);
AddGenericPictureAction("contrast", minValue: 0, maxValue: 100);
@@ -90,9 +103,14 @@ namespace ColorControl
AddGenericPictureAction("pictureMode", typeof(PictureMode));
AddGenericPictureAction("colorGamut", typeof(ColorGamut));
AddGenericPictureAction("dynamicContrast", typeof(OffToAuto));
//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("ambientLightCompensation", typeof(OffToAuto2));
//AddGenericPictureAction("truMotionMode", typeof(TruMotionMode));
AddGenericPictureAction("motionProOLED", typeof(OffToHigh));
AddGenericPictureAction("uhdDeepColorHDMI1", typeof(OffToOn), category: "other");
@@ -104,9 +122,10 @@ namespace ColorControl
AddGenericPictureAction("gameOptimizationHDMI2", typeof(OffToOn), category: "other");
AddGenericPictureAction("gameOptimizationHDMI3", typeof(OffToOn), category: "other");
AddGenericPictureAction("gameOptimizationHDMI4", typeof(OffToOn), category: "other");
AddGenericPictureAction("adjustingLuminance", minValue: -50, maxValue: 50);
}
private void AddInvokableAction(string name, Func<Dictionary<string, string>, bool> function)
private void AddInvokableAction(string name, Func<Dictionary<string, object>, bool> function)
{
var action = new InvokableAction
{
@@ -122,7 +141,7 @@ namespace ColorControl
var action = new InvokableAction
{
Name = name,
Function = new Func<Dictionary<string, string>, bool>(GenericPictureAction),
Function = new Func<Dictionary<string, object>, bool>(GenericPictureAction),
EnumType = type,
MinValue = minValue,
MaxValue = maxValue,
@@ -140,36 +159,56 @@ namespace ColorControl
public async Task<bool> Connect(int retries = 3)
{
var locked = _connectSemaphore.CurrentCount == 0;
await _connectSemaphore.WaitAsync();
try
{
DisposeConnection();
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries);
//Test();
//_lgTvApi.Test3();
if (_lgTvApi != null)
if (locked && _lgTvApi != null)
{
var info = await _lgTvApi.GetSystemInfo("modelName");
if (info != null)
return true;
}
try
{
DisposeConnection();
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries);
//Test();
//_lgTvApi.Test3();
if (_lgTvApi != null)
{
ModelName = info.modelName;
}
var info = await _lgTvApi.GetSystemInfo("modelName");
if (info != null)
{
ModelName = info.modelName;
}
//await _lgTvApi.SubscribeVolume(VolumeChanged);
await _lgTvApi.SubscribePowerState(PowerStateChanged);
await _lgTvApi.SubscribePictureSettings(PictureSettingsChanged);
//var result = await GetPictureSettings();
//await _lgTvApi.SubscribeVolume(VolumeChanged);
await _lgTvApi.SubscribePowerState(PowerStateChanged);
//await _lgTvApi.SetSystemSettings("adjustingLuminance", new[] { 0, 0, -5, -10, -15, -20, -25, -30, -35, -40, -45, -50, -50, -50, -40, -30, -20, -10, 10, 20, 30, 50 });
//await _lgTvApi.SetSystemSettings("adjustingLuminance", new[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
//await _lgTvApi.SetInput("HDMI_1");
//await Task.Delay(2000);
//await _lgTvApi.SetConfig("com.palm.app.settings.enableHdmiPcLabel", true);
//await _lgTvApi.SetInput("HDMI_2");
//await _lgTvApi.SetInput("HDMI_1");
//await Task.Delay(2000);
//await _lgTvApi.SetConfig("com.palm.app.settings.enableHdmiPcLabel", true);
//await _lgTvApi.SetInput("HDMI_2");
}
return _lgTvApi != null;
}
catch (Exception ex)
{
string logMessage = ex.ToLogString(Environment.StackTrace);
Logger.Error($"Error while connecting to {IpAddress}: {logMessage}");
return false;
}
return _lgTvApi != null;
}
catch (Exception ex)
finally
{
string logMessage = ex.ToLogString(Environment.StackTrace);
Logger.Error($"Error while connecting to {IpAddress}: {logMessage}");
return false;
_connectSemaphore.Release();
}
}
@@ -177,6 +216,26 @@ namespace ColorControl
{
return true;
}
public bool PictureSettingsChanged(dynamic payload)
{
if (payload.settings != null)
{
var settings = payload.settings;
if (settings.backlight != null)
{
PictureSettings.Backlight = int.Parse(settings.backlight.Value);
}
if (settings.contrast != null)
{
PictureSettings.Contrast = int.Parse(settings.contrast.Value);
}
}
return true;
}
public bool PowerStateChanged(dynamic payload)
{
Logger.Debug($"[{Name}] Power state change: {JsonConvert.SerializeObject(payload)}");
@@ -193,6 +252,7 @@ namespace ColorControl
if (payload.processing == null && (DateTimeOffset.Now - _poweredOffViaAppDateTime).TotalMilliseconds > 500)
{
PoweredOffViaApp = false;
PoweredOffBy = PowerOffSource.Unknown;
_poweredOffViaAppDateTime = DateTimeOffset.MinValue;
}
}
@@ -313,7 +373,7 @@ namespace ColorControl
{
var keyValue = keySpec[0].Split('(');
key = keyValue[0];
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(',');
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
}
var action = _invokableActions.FirstOrDefault(a => a.Name.Equals(key, StringComparison.OrdinalIgnoreCase));
if (action != null)
@@ -350,9 +410,9 @@ namespace ColorControl
if (parameters?.Length > 0)
{
var keyValues = new Dictionary<string, string> {
var keyValues = new Dictionary<string, object> {
{ "name", action.Name },
{ "value", parameters[0] },
{ "value", parameters },
{ "category", action.Category }
};
@@ -551,6 +611,12 @@ namespace ColorControl
IsCustom = true;
}
public bool IsUsingHDRPictureMode()
{
// Temporary workaround because I cannot read the picture mode at this time
return PictureSettings.Backlight == 100 && PictureSettings.Contrast == 100;
}
internal async Task SetBacklight(int backlight)
{
await _lgTvApi.SetSystemSettings("backlight", backlight.ToString());
@@ -572,21 +638,27 @@ namespace ColorControl
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", "pictureMode", "colorGamut", "dynamicContrast", "peakBrightness", "smoothGradation", "energySaving", "motionProOLED" };
var keys = new[] { "backlight", "brightness", "contrast", "color" };
return await _lgTvApi.GetSystemSettings("picture", keys);
}
private bool WakeAction(Dictionary<string, string> parameters)
private bool WakeAction(Dictionary<string, object> parameters)
{
return Wake();
}
private bool GenericPictureAction(Dictionary<string, string> parameters)
private bool GenericPictureAction(Dictionary<string, object> parameters)
{
var settingName = parameters["name"];
var value = parameters["value"];
var category = parameters["category"];
var settingName = parameters["name"].ToString();
var stringValues = parameters["value"] as string[];
var category = parameters["category"].ToString();
object value = stringValues[0];
if (stringValues.Length > 1)
{
value = stringValues.Select(s => int.Parse(s)).ToArray();
}
var task = _lgTvApi.SetSystemSettings(settingName, value, category);
Utils.WaitForTask(task);
+7 -2
View File
@@ -20,7 +20,7 @@ namespace ColorControl
[JsonIgnore]
public int Y { get; set; }
public LgPreset()
public LgPreset() : base()
{
steps = new List<string>();
}
@@ -46,7 +46,7 @@ namespace ColorControl
public static List<string> GetColumnNames()
{
return new List<string>() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut" };
return new List<string>() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut", "Trigger" };
}
public override List<string> GetDisplayValues(Config config = null)
@@ -90,6 +90,11 @@ namespace ColorControl
values.Add(GetStepsDisplay());
values.Add(shortcut);
if (Triggers.Any())
{
values.Add(Triggers.First().ToString());
}
return values;
}
+199 -49
View File
@@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -23,7 +22,20 @@ namespace ColorControl
ShutDown = 4,
StandBy = 5
}
internal class ProcessMonitorContext
{
public List<LgDevice> Devices { get; set; }
public bool WasFullScreen { get; set; }
public Process[] StartedProcesses { get; set; }
public Process[] StoppedProcesses { get; set; }
public Process[] RunningProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
public Process ForegroundProcess { get; set; }
public bool ForegroundProcessIsFullScreen { get; set; }
public string LastFullScreenProcessName = string.Empty;
}
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public static string LgListAppsJson = "listApps.json";
@@ -52,7 +64,7 @@ namespace ColorControl
private int _poweredOffByScreenSaverProcessId;
private Task _monitorTask;
private int _monitorTaskCounter;
public LgService(string dataPath, bool allowPowerOn) : base(dataPath)
{
_allowPowerOn = allowPowerOn;
@@ -352,7 +364,11 @@ namespace ColorControl
return false;
}
return await device.ExecutePreset(preset, reconnect);
var result = await device.ExecutePreset(preset, reconnect);
_lastAppliedPreset = preset;
return result;
}
public LgDevice GetPresetDevice(LgPreset preset)
@@ -521,7 +537,7 @@ namespace ColorControl
private void MonitorProcesses()
{
var enableMonitoring = Devices.Any(d => d.PowerSwitchOnScreenSaver);
var enableMonitoring = ShouldMonitorProcesses();
if (enableMonitoring && _monitorTask == null)
{
_monitorTask = CheckProcesses();
@@ -532,22 +548,35 @@ namespace ColorControl
}
}
public bool ShouldMonitorProcesses()
{
return Devices.Any(d => d.PowerSwitchOnScreenSaver) || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None));
}
public async Task CheckProcesses()
{
var wasConnected = Devices.Any(d => d.PowerSwitchOnScreenSaver && d.IsConnected());
_monitorTaskCounter++;
var validCounter = _monitorTaskCounter;
var lastProcessId = 0;
//const int minBacklight = 30;
//const int maxBacklight = 50;
//var lastBacklight = minBacklight;
while (Devices.Any(d => d.PowerSwitchOnScreenSaver) && validCounter == _monitorTaskCounter)
var monitorContext = new ProcessMonitorContext();
//Process[] lastProcesses = null;
while (validCounter == _monitorTaskCounter)
{
await Task.Delay(500);
await Task.Delay(_poweredOffByScreenSaver ? 500 : 1000);
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)));
if (!applicableDevices.Any())
{
break;
}
if (_poweredOffByScreenSaver && !UserSessionInfo.UserLocalSession)
{
continue;
@@ -571,78 +600,112 @@ namespace ColorControl
continue;
}
var devices = Devices.Where(d => d.PowerSwitchOnScreenSaver && d.IsConnected()).ToList();
var connectedDevices = applicableDevices.Where(d => d.IsConnected()).ToList();
if (!devices.Any())
if (!connectedDevices.Any())
{
if (wasConnected)
{
Logger.Debug("Screensaver check: TV(s) where connected, but not any longer");
Logger.Debug("Process monitor: TV(s) where connected, but not any longer");
wasConnected = false;
continue;
}
else
{
var devices = applicableDevices.ToList();
foreach (var device in devices)
{
Logger.Debug($"Process monitor: test connection with {device.Name}...");
var test = await device.TestConnection();
Logger.Debug("Process monitor: test connection result: " + test);
}
connectedDevices = applicableDevices.Where(d => d.IsConnected()).ToList();
if (!connectedDevices.Any())
{
continue;
}
}
continue;
}
if (!wasConnected)
{
Logger.Debug("Screensaver check: TV(s) where not connected, but connection has now been established");
Logger.Debug("Process monitor: TV(s) where not connected, but connection has now been established");
wasConnected = true;
}
//if (Utils.IsForegroundFullScreen())
monitorContext.Devices = connectedDevices;
monitorContext.RunningProcesses = processes;
//if (lastProcesses != null)
//{
// if (lastBacklight != maxBacklight)
// {
// SelectedDevice?.SetBacklight(maxBacklight);
// lastBacklight = maxBacklight;
// }
// 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 if (lastBacklight != minBacklight)
//else
//{
// SelectedDevice?.SetBacklight(minBacklight);
// lastBacklight = minBacklight;
// 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 (process != null)
{
continue;
}
var parent = process.Parent();
var parent = process.Parent();
if (parent?.ProcessName.Contains("winlogon") ?? false)
{
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
try
if (parent?.ProcessName.Contains("winlogon") ?? false)
{
foreach (var device in devices)
Logger.Debug($"Screensaver started: {process.ProcessName}, parent: {parent.ProcessName}");
try
{
Logger.Debug($"Screensaver check: test connection with {device.Name}...");
var test = await device.TestConnection();
Logger.Debug("Screensaver check: test connection result: " + test);
if (!test)
foreach (var device in connectedDevices)
{
continue;
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}");
}
Logger.Debug($"Screensaver check: powering off tv {device.Name} because of screensaver");
_poweredOffByScreenSaver = await device.PowerOff();
_poweredOffByScreenSaverProcessId = process.Id;
lastProcessId = 0;
}
catch (Exception e)
{
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
}
_poweredOffByScreenSaverProcessId = process.Id;
lastProcessId = 0;
}
catch (Exception e)
else if (process.Id != lastProcessId)
{
Logger.Error($"Screensaver check: can't power off: " + e.ToLogString());
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
lastProcessId = process.Id;
}
}
else if (process.Id != lastProcessId)
else
{
Logger.Debug($"Screensaver started: {process.ProcessName}, but invalid parent: {parent?.ProcessName ?? "no parent"}");
lastProcessId = process.Id;
await ExecuteProcessPresets(monitorContext);
}
}
catch (Exception ex)
@@ -652,6 +715,93 @@ namespace ColorControl
}
}
private async Task ExecuteProcessPresets(ProcessMonitorContext context)
{
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == PresetTriggerType.ProcessSwitch));
if (!presets.Any())
{
return;
}
context.IsNotificationDisabled = Utils.IsNotificationDisabled();
var (processId, isFullScreen) = Utils.GetForegroundProcessIdAndIfFullScreen();
if (processId > 0)
{
context.ForegroundProcess = context.RunningProcesses.FirstOrDefault(p => p.Id == processId);
context.ForegroundProcessIsFullScreen = isFullScreen;
}
else
{
context.ForegroundProcess = null;
context.ForegroundProcessIsFullScreen = false;
}
var changedProcesses = new List<Process>();
if (context.ForegroundProcess != null)
{
changedProcesses.Add(context.ForegroundProcess);
}
if (context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen)
{
if (!context.LastFullScreenProcessName.Equals(context.ForegroundProcess.ProcessName))
{
context.LastFullScreenProcessName = context.ForegroundProcess.ProcessName;
//Logger.Debug($"Foreground fullscreen app detected: {context.ForegroundProcess.ProcessName}");
}
presets = presets.Where(p => p.Triggers.Any(t => t.Conditions.HasFlag(PresetConditionType.FullScreen)));
context.WasFullScreen = true;
}
else if (context.WasFullScreen)
{
presets = presets.Where(p => p.Triggers.Any(t => !t.Conditions.HasFlag(PresetConditionType.FullScreen)));
context.WasFullScreen = false;
context.LastFullScreenProcessName = string.Empty;
}
var isHDRActive = context.Devices.Any(d => d.IsUsingHDRPictureMode());
var triggerContext = new PresetTriggerContext
{
IsHDRActive = isHDRActive,
ForegroundProcess = context.ForegroundProcess,
ForegroundProcessIsFullScreen = context.ForegroundProcessIsFullScreen,
ChangedProcesses = changedProcesses,
IsNotificationDisabled = context.IsNotificationDisabled
};
var toApplyPresets = new List<LgPreset>();
foreach (var preset in presets)
{
if (preset.Triggers.Any(t => t.TriggerActive(triggerContext)))
{
toApplyPresets.Add(preset);
}
}
if (toApplyPresets.Any())
{
LgPreset toApplyPreset;
if (toApplyPresets.Count > 1)
{
toApplyPreset = toApplyPresets.FirstOrDefault(p => p.Triggers.Any(t => !t.IncludedProcesses.Contains("*")));
toApplyPreset = toApplyPreset != null ? toApplyPreset : toApplyPresets.First();
}
else
{
toApplyPreset = toApplyPresets.First();
}
if (_lastAppliedPreset != toApplyPreset)
{
await ApplyPreset(toApplyPreset);
}
}
}
internal void AddCustomDevice(LgDevice device)
{
Devices.Add(device);
+130 -13
View File
@@ -104,6 +104,13 @@
this.lvAmdPresets = new System.Windows.Forms.ListView();
this.tabLG = new System.Windows.Forms.TabPage();
this.scLgController = new System.Windows.Forms.SplitContainer();
this.lblLgPresetExcludedProcesses = new System.Windows.Forms.Label();
this.edtLgPresetExcludedProcesses = new System.Windows.Forms.TextBox();
this.lblLgPresetIncludedProcesses = new System.Windows.Forms.Label();
this.edtLgPresetIncludedProcesses = new System.Windows.Forms.TextBox();
this.lblLgPresetTriggerCondition = new System.Windows.Forms.Label();
this.cbxLgPresetTrigger = new System.Windows.Forms.ComboBox();
this.lblLgPresetTrigger = new System.Windows.Forms.Label();
this.btnLgExpert = new System.Windows.Forms.Button();
this.mnuLgExpert = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mnuLgOLEDMotionPro = new System.Windows.Forms.ToolStripMenuItem();
@@ -138,6 +145,8 @@
this.mnuLgButtons = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mnuLgRcButtons = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLgActions = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLgNvPresets = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLgAmdPresets = new System.Windows.Forms.ToolStripMenuItem();
this.btnCloneLg = new System.Windows.Forms.Button();
this.lblStepsLg = new System.Windows.Forms.Label();
this.btnLgDeviceFilterRefresh = new System.Windows.Forms.Button();
@@ -182,8 +191,8 @@
this.lbPlugins = new System.Windows.Forms.ListBox();
this.label7 = new System.Windows.Forms.Label();
this.lblInfo = new System.Windows.Forms.Label();
this.mnuLgNvPresets = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLgAmdPresets = new System.Windows.Forms.ToolStripMenuItem();
this.edtLgPresetTriggerConditions = new System.Windows.Forms.TextBox();
this.btnLgPresetEditTriggerConditions = new System.Windows.Forms.Button();
this.tcMain.SuspendLayout();
this.tabNVIDIA.SuspendLayout();
this.mnuNvPresets.SuspendLayout();
@@ -941,6 +950,15 @@
//
// scLgController.Panel1
//
this.scLgController.Panel1.Controls.Add(this.btnLgPresetEditTriggerConditions);
this.scLgController.Panel1.Controls.Add(this.edtLgPresetTriggerConditions);
this.scLgController.Panel1.Controls.Add(this.lblLgPresetExcludedProcesses);
this.scLgController.Panel1.Controls.Add(this.edtLgPresetExcludedProcesses);
this.scLgController.Panel1.Controls.Add(this.lblLgPresetIncludedProcesses);
this.scLgController.Panel1.Controls.Add(this.edtLgPresetIncludedProcesses);
this.scLgController.Panel1.Controls.Add(this.lblLgPresetTriggerCondition);
this.scLgController.Panel1.Controls.Add(this.cbxLgPresetTrigger);
this.scLgController.Panel1.Controls.Add(this.lblLgPresetTrigger);
this.scLgController.Panel1.Controls.Add(this.btnLgExpert);
this.scLgController.Panel1.Controls.Add(this.chkLgRemoteControlShow);
this.scLgController.Panel1.Controls.Add(this.label3);
@@ -977,6 +995,75 @@
this.scLgController.SplitterDistance = 767;
this.scLgController.TabIndex = 43;
//
// lblLgPresetExcludedProcesses
//
this.lblLgPresetExcludedProcesses.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblLgPresetExcludedProcesses.AutoSize = true;
this.lblLgPresetExcludedProcesses.Location = new System.Drawing.Point(407, 375);
this.lblLgPresetExcludedProcesses.Name = "lblLgPresetExcludedProcesses";
this.lblLgPresetExcludedProcesses.Size = new System.Drawing.Size(105, 13);
this.lblLgPresetExcludedProcesses.TabIndex = 52;
this.lblLgPresetExcludedProcesses.Text = "Excluded processes:";
//
// edtLgPresetExcludedProcesses
//
this.edtLgPresetExcludedProcesses.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.edtLgPresetExcludedProcesses.Enabled = false;
this.edtLgPresetExcludedProcesses.Location = new System.Drawing.Point(515, 373);
this.edtLgPresetExcludedProcesses.Name = "edtLgPresetExcludedProcesses";
this.edtLgPresetExcludedProcesses.Size = new System.Drawing.Size(248, 20);
this.edtLgPresetExcludedProcesses.TabIndex = 51;
//
// lblLgPresetIncludedProcesses
//
this.lblLgPresetIncludedProcesses.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblLgPresetIncludedProcesses.AutoSize = true;
this.lblLgPresetIncludedProcesses.Location = new System.Drawing.Point(407, 349);
this.lblLgPresetIncludedProcesses.Name = "lblLgPresetIncludedProcesses";
this.lblLgPresetIncludedProcesses.Size = new System.Drawing.Size(102, 13);
this.lblLgPresetIncludedProcesses.TabIndex = 50;
this.lblLgPresetIncludedProcesses.Text = "Included processes:";
//
// edtLgPresetIncludedProcesses
//
this.edtLgPresetIncludedProcesses.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.edtLgPresetIncludedProcesses.Enabled = false;
this.edtLgPresetIncludedProcesses.Location = new System.Drawing.Point(515, 347);
this.edtLgPresetIncludedProcesses.Name = "edtLgPresetIncludedProcesses";
this.edtLgPresetIncludedProcesses.Size = new System.Drawing.Size(248, 20);
this.edtLgPresetIncludedProcesses.TabIndex = 49;
//
// lblLgPresetTriggerCondition
//
this.lblLgPresetTriggerCondition.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblLgPresetTriggerCondition.AutoSize = true;
this.lblLgPresetTriggerCondition.Location = new System.Drawing.Point(407, 322);
this.lblLgPresetTriggerCondition.Name = "lblLgPresetTriggerCondition";
this.lblLgPresetTriggerCondition.Size = new System.Drawing.Size(54, 13);
this.lblLgPresetTriggerCondition.TabIndex = 47;
this.lblLgPresetTriggerCondition.Text = "Condition:";
//
// cbxLgPresetTrigger
//
this.cbxLgPresetTrigger.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.cbxLgPresetTrigger.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbxLgPresetTrigger.Enabled = false;
this.cbxLgPresetTrigger.FormattingEnabled = true;
this.cbxLgPresetTrigger.Location = new System.Drawing.Point(515, 292);
this.cbxLgPresetTrigger.Name = "cbxLgPresetTrigger";
this.cbxLgPresetTrigger.Size = new System.Drawing.Size(248, 21);
this.cbxLgPresetTrigger.TabIndex = 46;
//
// lblLgPresetTrigger
//
this.lblLgPresetTrigger.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblLgPresetTrigger.AutoSize = true;
this.lblLgPresetTrigger.Location = new System.Drawing.Point(407, 295);
this.lblLgPresetTrigger.Name = "lblLgPresetTrigger";
this.lblLgPresetTrigger.Size = new System.Drawing.Size(43, 13);
this.lblLgPresetTrigger.TabIndex = 45;
this.lblLgPresetTrigger.Text = "Trigger:";
//
// btnLgExpert
//
this.btnLgExpert.ContextMenuStrip = this.mnuLgExpert;
@@ -1306,21 +1393,33 @@
this.mnuLgNvPresets,
this.mnuLgAmdPresets});
this.mnuLgButtons.Name = "mnuLgButtons";
this.mnuLgButtons.Size = new System.Drawing.Size(181, 114);
this.mnuLgButtons.Size = new System.Drawing.Size(153, 92);
this.mnuLgButtons.Opening += new System.ComponentModel.CancelEventHandler(this.mnuLgButtons_Opening);
//
// mnuLgRcButtons
//
this.mnuLgRcButtons.Name = "mnuLgRcButtons";
this.mnuLgRcButtons.Size = new System.Drawing.Size(180, 22);
this.mnuLgRcButtons.Size = new System.Drawing.Size(152, 22);
this.mnuLgRcButtons.Text = "Buttons";
//
// mnuLgActions
//
this.mnuLgActions.Name = "mnuLgActions";
this.mnuLgActions.Size = new System.Drawing.Size(180, 22);
this.mnuLgActions.Size = new System.Drawing.Size(152, 22);
this.mnuLgActions.Text = "Actions";
//
// mnuLgNvPresets
//
this.mnuLgNvPresets.Name = "mnuLgNvPresets";
this.mnuLgNvPresets.Size = new System.Drawing.Size(152, 22);
this.mnuLgNvPresets.Text = "NVIDIA presets";
//
// mnuLgAmdPresets
//
this.mnuLgAmdPresets.Name = "mnuLgAmdPresets";
this.mnuLgAmdPresets.Size = new System.Drawing.Size(152, 22);
this.mnuLgAmdPresets.Text = "AMD presets";
//
// btnCloneLg
//
this.btnCloneLg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
@@ -1832,17 +1931,26 @@
this.lblInfo.TabIndex = 1;
this.lblInfo.Text = "Info";
//
// mnuLgNvPresets
// edtLgPresetTriggerConditions
//
this.mnuLgNvPresets.Name = "mnuLgNvPresets";
this.mnuLgNvPresets.Size = new System.Drawing.Size(180, 22);
this.mnuLgNvPresets.Text = "NVIDIA presets";
this.edtLgPresetTriggerConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.edtLgPresetTriggerConditions.Enabled = false;
this.edtLgPresetTriggerConditions.Location = new System.Drawing.Point(515, 319);
this.edtLgPresetTriggerConditions.Name = "edtLgPresetTriggerConditions";
this.edtLgPresetTriggerConditions.ReadOnly = true;
this.edtLgPresetTriggerConditions.Size = new System.Drawing.Size(167, 20);
this.edtLgPresetTriggerConditions.TabIndex = 53;
//
// mnuLgAmdPresets
// btnLgPresetEditTriggerConditions
//
this.mnuLgAmdPresets.Name = "mnuLgAmdPresets";
this.mnuLgAmdPresets.Size = new System.Drawing.Size(180, 22);
this.mnuLgAmdPresets.Text = "AMD presets";
this.btnLgPresetEditTriggerConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnLgPresetEditTriggerConditions.Location = new System.Drawing.Point(689, 317);
this.btnLgPresetEditTriggerConditions.Name = "btnLgPresetEditTriggerConditions";
this.btnLgPresetEditTriggerConditions.Size = new System.Drawing.Size(75, 23);
this.btnLgPresetEditTriggerConditions.TabIndex = 54;
this.btnLgPresetEditTriggerConditions.Text = "Edit...";
this.btnLgPresetEditTriggerConditions.UseVisualStyleBackColor = true;
this.btnLgPresetEditTriggerConditions.Click += new System.EventHandler(this.btnLgPresetEditTriggerConditions_Click);
//
// MainForm
//
@@ -2055,6 +2163,15 @@
private System.Windows.Forms.ToolStripSeparator miLgExpertSeparator1;
private System.Windows.Forms.ToolStripMenuItem mnuLgNvPresets;
private System.Windows.Forms.ToolStripMenuItem mnuLgAmdPresets;
private System.Windows.Forms.ComboBox cbxLgPresetTrigger;
private System.Windows.Forms.Label lblLgPresetTrigger;
private System.Windows.Forms.Label lblLgPresetTriggerCondition;
private System.Windows.Forms.Label lblLgPresetExcludedProcesses;
private System.Windows.Forms.TextBox edtLgPresetExcludedProcesses;
private System.Windows.Forms.Label lblLgPresetIncludedProcesses;
private System.Windows.Forms.TextBox edtLgPresetIncludedProcesses;
private System.Windows.Forms.Button btnLgPresetEditTriggerConditions;
private System.Windows.Forms.TextBox edtLgPresetTriggerConditions;
}
}
+120 -65
View File
@@ -1092,6 +1092,11 @@ namespace ColorControl
btnLgAddButton.Enabled = enabled;
cbxLgApps.Enabled = enabled;
btnDeleteLg.Enabled = enabled;
cbxLgPresetTrigger.Enabled = enabled;
edtLgPresetTriggerConditions.Enabled = enabled;
btnLgPresetEditTriggerConditions.Enabled = enabled;
edtLgPresetIncludedProcesses.Enabled = enabled;
edtLgPresetExcludedProcesses.Enabled = enabled;
var preset = GetSelectedLgPreset();
@@ -1116,6 +1121,16 @@ namespace ColorControl
}
cbxLgPresetDevice.SelectedIndex = index;
var trigger = preset.Triggers.FirstOrDefault();
Utils.SetComboBoxEnumIndex(cbxLgPresetTrigger, (int)(trigger?.Trigger ?? PresetTriggerType.None));
edtLgPresetTriggerConditions.Text = Utils.GetDescriptions<PresetConditionType>(trigger != null ? (int)trigger.Conditions : 0).Join(", ");
edtLgPresetTriggerConditions.Tag = trigger != null ? (int)trigger.Conditions : 0;
edtLgPresetIncludedProcesses.Text = trigger?.IncludedProcesses?.Join(", ") ?? string.Empty;
edtLgPresetExcludedProcesses.Text = trigger?.ExcludedProcesses?.Join(", ") ?? string.Empty;
}
else
{
@@ -1124,6 +1139,11 @@ namespace ColorControl
edtShortcutLg.Text = string.Empty;
edtStepsLg.Text = string.Empty;
cbxLgPresetDevice.SelectedIndex = -1;
cbxLgPresetTrigger.SelectedIndex = -1;
edtLgPresetTriggerConditions.Text = string.Empty;
edtLgPresetTriggerConditions.Tag = 0;
edtLgPresetIncludedProcesses.Text = string.Empty;
edtLgPresetExcludedProcesses.Text = string.Empty;
}
}
@@ -1177,6 +1197,17 @@ namespace ColorControl
preset.appId = lgApp.appId;
}
var triggerType = Utils.GetComboBoxEnumItem<PresetTriggerType>(cbxLgPresetTrigger);
preset.UpdateTrigger(triggerType,
(PresetConditionType)edtLgPresetTriggerConditions.Tag,
edtLgPresetIncludedProcesses.Text,
edtLgPresetExcludedProcesses.Text);
if (triggerType != PresetTriggerType.None)
{
_lgService.InstallEventHandlers();
}
var shortcutChanged = !shortcut.Equals(preset.shortcut);
if (shortcutChanged)
{
@@ -1184,27 +1215,8 @@ namespace ColorControl
}
var text = edtStepsLg.Text;
var clearSteps = string.IsNullOrEmpty(text);
preset.steps.Clear();
if (!clearSteps)
{
//text = text.Replace(" ", string.Empty);
var steps = text.Split(',');
for (var i = 0; i < steps.Length; i++)
{
var step = steps[i];
if (step.IndexOf("(") > -1)
{
steps[i] = step.Trim();
}
else
{
steps[i] = step.Replace(" ", string.Empty);
}
}
preset.steps.AddRange(steps);
}
Utils.ParseWords(preset.steps, text);
AddOrUpdateItemLg();
@@ -1262,27 +1274,7 @@ namespace ColorControl
{
if (tcMain.SelectedTab == tabLG)
{
if (cbxLgDevices.Items.Count == 0 && _lgService != null)
{
var devices = _lgService.Devices;
if (devices == null || !devices.Any())
{
RefreshLgDevices();
}
else
{
FillLgDevices();
}
if (scLgController.Panel2.Controls.Count == 0)
{
var rcPanel = new RemoteControlPanel(_lgService, _lgService.GetRemoteControlButtons());
rcPanel.Parent = scLgController.Panel2;
rcPanel.Dock = DockStyle.Fill;
}
chkLgRemoteControlShow.Checked = _lgService.Config.ShowRemoteControl;
scLgController.Panel2Collapsed = !_lgService.Config.ShowRemoteControl;
}
InitLgTab();
}
else if (tcMain.SelectedTab == tabLog)
{
@@ -1294,25 +1286,57 @@ namespace ColorControl
}
else if (tcMain.SelectedTab == tabOptions)
{
grpNvidiaOptions.Visible = _nvService != null;
if (grpNvidiaOptions.Visible)
InitOptionsTab();
}
}
private void InitLgTab()
{
if (cbxLgDevices.Items.Count == 0 && _lgService != null)
{
var devices = _lgService.Devices;
if (devices == null || !devices.Any())
{
var firstTime = cbxDitheringBitDepth.Items.Count == 0;
RefreshLgDevices();
}
else
{
FillLgDevices();
}
if (firstTime)
{
cbxDitheringBitDepth.Items.AddRange(Utils.GetDescriptions<NvDitherBits>().ToArray());
cbxDitheringMode.Items.AddRange(Utils.GetDescriptions<NvDitherMode>().ToArray());
}
if (scLgController.Panel2.Controls.Count == 0)
{
var rcPanel = new RemoteControlPanel(_lgService, _lgService.GetRemoteControlButtons());
rcPanel.Parent = scLgController.Panel2;
rcPanel.Dock = DockStyle.Fill;
}
chkLgRemoteControlShow.Checked = _lgService.Config.ShowRemoteControl;
scLgController.Panel2Collapsed = !_lgService.Config.ShowRemoteControl;
var preset = _nvService.GetLastAppliedPreset() ?? GetSelectedNvPreset();
if (firstTime || preset != null)
{
chkDitheringEnabled.Checked = preset?.ditheringEnabled ?? true;
cbxDitheringBitDepth.SelectedIndex = (int)(preset?.ditheringBits ?? (int)NvDitherBits.Bits8);
cbxDitheringMode.SelectedIndex = (int)(preset?.ditheringMode ?? (int)NvDitherMode.Temporal);
FillGradient();
}
Utils.BuildComboBox<PresetTriggerType>(cbxLgPresetTrigger, PresetTriggerType.Resume, PresetTriggerType.Screensaver, PresetTriggerType.Shutdown, PresetTriggerType.Standby, PresetTriggerType.Startup);
}
}
private void InitOptionsTab()
{
grpNvidiaOptions.Visible = _nvService != null;
if (grpNvidiaOptions.Visible)
{
var firstTime = cbxDitheringBitDepth.Items.Count == 0;
if (firstTime)
{
cbxDitheringBitDepth.Items.AddRange(Utils.GetDescriptions<NvDitherBits>().ToArray());
cbxDitheringMode.Items.AddRange(Utils.GetDescriptions<NvDitherMode>().ToArray());
}
var preset = _nvService.GetLastAppliedPreset() ?? GetSelectedNvPreset();
if (firstTime || preset != null)
{
chkDitheringEnabled.Checked = preset?.ditheringEnabled ?? true;
cbxDitheringBitDepth.SelectedIndex = (int)(preset?.ditheringBits ?? (int)NvDitherBits.Bits8);
cbxDitheringMode.SelectedIndex = (int)(preset?.ditheringMode ?? (int)NvDitherMode.Temporal);
FillGradient();
}
}
}
@@ -1630,7 +1654,7 @@ namespace ColorControl
{
lines = File.ReadAllLines(filename);
}
var reversedLines = lines.Reverse().ToList();
var reversedLines = lines.ToList();
var builder = new StringBuilder();
reversedLines.ForEach(line => builder.AppendLine(line));
edtLog.Text = builder.ToString();
@@ -1729,7 +1753,7 @@ namespace ColorControl
return;
}
value = values.First();
value = values.First().Value.ToString();
}
}
else
@@ -1754,7 +1778,7 @@ namespace ColorControl
return;
}
value = values.First();
value = values.First().Value.ToString();
}
if (!string.IsNullOrEmpty(value))
@@ -2001,7 +2025,7 @@ Do you want to continue?";
var preset = GetSelectedNvPreset();
preset.ditheringBits = uint.Parse(item.Tag.ToString());
preset.ditheringMode = uint.Parse(item.Tag.ToString());
AddOrUpdateItem();
@@ -2468,7 +2492,7 @@ Do you want to continue?";
var values = MessageForms.ShowDialog("Add tv", new[] { "Name", "Ip-address", "MAC-address" }, ValidateAddDevice);
if (values.Any())
{
var device = new LgDevice(values[0], values[1], values[2]);
var device = new LgDevice(values[0].Value.ToString(), values[1].Value.ToString(), values[2].Value.ToString());
var form = MessageForms.ShowProgress("Connecting to device...");
@@ -2497,9 +2521,9 @@ Do you want to continue?";
}
}
private string ValidateAddDevice(List<string> values)
private string ValidateAddDevice(IEnumerable<MessageForms.FieldDefinition> values)
{
if (values.Any(v => string.IsNullOrEmpty(v)))
if (values.Any(v => string.IsNullOrEmpty(v.Value?.ToString())))
{
return "Please fill in all the fields";
}
@@ -2538,7 +2562,7 @@ Do you want to continue?";
var device = _lgService.GetPresetDevice(preset);
var actions = device?.GetInvokableActions();
foreach (var action in actions.Where(a => a.Category == null || !a.Category.Equals("other")))
foreach (var action in actions)
{
var text = action.Name;
@@ -2694,5 +2718,36 @@ Do you want to continue?"
cbxLgApps.SelectedIndex = -1;
}
}
private void btnLgPresetEditTriggerConditions_Click(object sender, EventArgs e)
{
var preset = GetSelectedLgPreset();
if (preset == null)
{
return;
}
var dropDownValues = Utils.GetDescriptions<PresetConditionType>(fromValue: 1);
var values = MessageForms.ShowDialog("Set trigger conditions", new[] {
new MessageForms.FieldDefinition
{
Label = "Set desired trigger conditions",
FieldType = MessageForms.FieldType.Flags,
Values = dropDownValues,
Value = edtLgPresetTriggerConditions.Tag ?? 0
}
});
if (!values.Any())
{
return;
}
var value = (PresetConditionType)values.First().Value;
edtLgPresetTriggerConditions.Tag = (PresetConditionType)values.First().Value;
edtLgPresetTriggerConditions.Text = Utils.GetDescriptions<PresetConditionType>((int)value).Join(", ");
}
}
}
+66 -8
View File
@@ -12,7 +12,8 @@ namespace ColorControl
{
Text,
Numeric,
DropDown
DropDown,
Flags
}
public class FieldDefinition
@@ -22,6 +23,7 @@ namespace ColorControl
public IEnumerable<string> Values { get; set; }
public decimal MinValue { get; set; }
public decimal MaxValue { get; set; }
public object Value { get; set; }
}
public static Form MainForm;
@@ -46,19 +48,19 @@ namespace ColorControl
return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
}
public static List<string> ShowDialog(string caption, IEnumerable<string> labels, Func<List<string>, string> validateFunc = null)
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<string> labels, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
{
var fieldDefinitions = labels.Select(l => new FieldDefinition
{
Label = l
});
}).ToList();
return ShowDialog(caption, fieldDefinitions, validateFunc);
}
public static List<string> ShowDialog(string caption, IEnumerable<FieldDefinition> fields, Func<List<string>, string> validateFunc = null)
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<FieldDefinition> fields, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
{
var values = new List<string>();
var values = new List<FieldDefinition>();
var prompt = new Form()
{
@@ -131,6 +133,35 @@ namespace ColorControl
control = comboBox;
break;
case FieldType.Flags:
var checkedListBox = new CheckedListBox
{
Left = 40,
Top = top,
Width = 200,
Height = 100
};
top += 80;
prompt.Height += 80;
if (field.Values != null && field.Values.Any())
{
var compoundValue = (int)field.Value;
var enumValue = 1;
foreach (var value in field.Values)
{
var isChecked = (compoundValue & enumValue) == enumValue;
checkedListBox.Items.Add(value, isChecked);
enumValue *= 2;
}
}
control = checkedListBox;
break;
default:
continue;
}
@@ -138,24 +169,51 @@ namespace ColorControl
top += 30;
prompt.Controls.Add(control);
boxes.Add(control);
control.Tag = field;
}
var confirmation = new Button() { Text = "OK", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 };
confirmation.Click += (sender, e) =>
{
values.AddRange(boxes.Select(b => b.Text.Trim()));
foreach (var box in boxes)
{
var field = (FieldDefinition)box.Tag;
switch (field.FieldType)
{
case FieldType.Flags:
var checkedListBox = (CheckedListBox)box;
var compoundValue = 0;
var enumValue = 1;
for (var i = 0; i < checkedListBox.Items.Count; i++)
{
compoundValue += checkedListBox.GetItemChecked(i) ? enumValue : 0;
enumValue *= 2;
}
field.Value = compoundValue;
break;
default:
field.Value = box.Text.Trim();
break;
}
}
if (validateFunc != null)
{
var message = validateFunc(values);
var message = validateFunc(fields);
if (!string.IsNullOrEmpty(message))
{
WarningOk(message);
values.Clear();
return;
}
}
values.AddRange(fields);
prompt.Close();
};
prompt.Controls.Add(confirmation);
+1 -1
View File
@@ -22,7 +22,7 @@ namespace ColorControl
public uint ditheringBits { get; set; }
public uint ditheringMode { get; set; }
public NvPreset()
public NvPreset() : base()
{
applyColorData = true;
applyDithering = false;
+17 -13
View File
@@ -290,26 +290,30 @@ namespace ColorControl
{
if (enabled != IsHDREnabled())
{
ToggleHDR();
CCD.SetHDRState(enabled, display.Name);
//ToggleHDR();
}
return;
}
var newMaster = new MasteringDisplayColorData();
var hdr = new HDRColorData(enabled ? ColorDataHDRMode.UHDA : ColorDataHDRMode.Off, newMaster, colorData?.ColorFormat, colorData?.DynamicRange, colorData?.ColorDepth);
CCD.SetHDRState(enabled, display.Name);
display.DisplayDevice.SetHDRColorData(hdr);
//var newMaster = new MasteringDisplayColorData();
//var hdr = new HDRColorData(enabled ? ColorDataHDRMode.UHDA : ColorDataHDRMode.Off, newMaster, colorData?.ColorFormat, colorData?.DynamicRange, colorData?.ColorDepth);
//display.DisplayDevice.SetHDRColorData(hdr);
// HDR will not always be disabled this way, then we can only disable it through the display settings
if (!enabled && IsHDREnabled())
{
ToggleHDR();
}
else if (enabled)
{
// Currectly there seems to be a bug that after enabling HDR via NVAPI, some settings are only applied upon opening the Display Settings...
////OpenDisplaySettings();
}
//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)
+131 -10
View File
@@ -1,36 +1,123 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace ColorControl
{
[Flags]
enum PresetTrigger
enum PresetTriggerType
{
[Description("None")]
None = 0,
[Description("Startup")]
Startup = 1,
[Description("Resume")]
Resume = 2,
[Description("Shutdown")]
Shutdown = 4,
Shutdown = 3,
[Description("Standby")]
Standby = 8,
Standby = 4,
[Description("Screensaver")]
Screensaver = 16,
[Description("Process start")]
ProcessStart = 32,
[Description("Process exit")]
ProcessExit = 64
Screensaver = 5,
[Description("Process switch")]
ProcessSwitch = 6,
}
[Flags]
enum PresetConditionType
{
[Description("None")]
None = 0,
[Description("SDR")]
SDR = 1,
[Description("HDR")]
HDR = 2,
[Description("Full Screen")]
FullScreen = 4,
[Description("Notifications Disabled")]
NotificationsDisabled = 8,
}
class PresetTrigger
{
public PresetTriggerType Trigger { get; set; }
public List<string> IncludedProcesses { get; set; }
public List<string> ExcludedProcesses { get; set; }
public PresetConditionType Conditions { get; set; }
public PresetTrigger()
{
IncludedProcesses = new List<string>();
ExcludedProcesses = new List<string>();
}
public bool TriggerActive(PresetTriggerContext context)
{
var active = Conditions == PresetConditionType.None || (Conditions.HasFlag(PresetConditionType.SDR) && !context.IsHDRActive) || (Conditions.HasFlag(PresetConditionType.HDR) && context.IsHDRActive);
var allProcesses = IncludedProcesses.Contains("*");
if (Trigger == PresetTriggerType.ProcessSwitch)
{
active = active && (allProcesses || (context.ChangedProcesses?.Any() ?? false));
if (active)
{
var included = allProcesses || context.ChangedProcesses.Any(cp => IncludedProcesses.Any(ip => cp.ProcessName.Equals(ip, StringComparison.OrdinalIgnoreCase)));
var excluded = context.ChangedProcesses.Any(cp => ExcludedProcesses.Any(ep => cp.ProcessName.Equals(ep, StringComparison.OrdinalIgnoreCase)));
var screenSizeCheck = (!Conditions.HasFlag(PresetConditionType.FullScreen) && !context.ForegroundProcessIsFullScreen) ||
(context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen &&
(allProcesses || IncludedProcesses.Any(ip => context.ForegroundProcess.ProcessName.Equals(ip, StringComparison.OrdinalIgnoreCase))));
var notificationsDisabledCheck = !Conditions.HasFlag(PresetConditionType.NotificationsDisabled) || context.IsNotificationDisabled;
active = included && !excluded && screenSizeCheck && notificationsDisabledCheck;
}
}
return active;
}
public override string ToString()
{
if (Trigger == PresetTriggerType.None)
{
return string.Empty;
}
return $"On {Trigger.GetDescription()} of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
}
public static string DisplayProcesses(IEnumerable<string> processes)
{
return processes.Contains("*") ? "all processes" : string.Join(", ", processes);
}
}
class PresetTriggerContext
{
public bool IsHDRActive { get; set; }
public Process ForegroundProcess { get; set; }
public bool ForegroundProcessIsFullScreen { get; set; }
public List<Process> ChangedProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
}
internal abstract class PresetBase
{
public string name { get; set; }
public string shortcut { get; set; }
public PresetTrigger Triggers { get; set; }
public List<PresetTrigger> Triggers { get; set; }
private int _id;
protected PresetBase()
{
Triggers = new List<PresetTrigger>();
}
public int id
{
get
@@ -68,5 +155,39 @@ namespace ColorControl
}
public abstract List<string> GetDisplayValues(Config config = null);
public void UpdateTrigger(PresetTriggerType triggerType, PresetConditionType conditions, string includedProcesses, string excludedProcesses)
{
if (!Triggers.Any())
{
Triggers.Add(new PresetTrigger());
}
var trigger = Triggers.First();
trigger.Trigger = triggerType;
trigger.Conditions = conditions;
if (trigger.IncludedProcesses == null)
{
trigger.IncludedProcesses = new List<string>();
}
if (trigger.ExcludedProcesses == null)
{
trigger.ExcludedProcesses = new List<string>();
}
Utils.ParseWords(trigger.IncludedProcesses, includedProcesses);
Utils.ParseWords(trigger.ExcludedProcesses, excludedProcesses);
}
public PresetTrigger AddTrigger()
{
if (!Triggers.Any())
{
Triggers.Add(new PresetTrigger());
}
return Triggers.First();
}
}
}
+2 -2
View File
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 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("3.3.0.0")]
[assembly: AssemblyFileVersion("3.3.0.0")]
[assembly: AssemblyVersion("4.0.0.0")]
[assembly: AssemblyFileVersion("4.0.0.0")]
+173 -13
View File
@@ -36,6 +36,23 @@ namespace ColorControl
Win = 8
}
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,
};
public const int WM_BRINGTOFRONT = NativeConstants.WM_USER + 1;
public const int ENUM_CURRENT_SETTINGS = -1;
@@ -75,6 +92,12 @@ namespace ColorControl
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeConsole();
[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);
//[DllImport("Wtsapi32.dll")]
//public extern static bool WTSRegisterSessionNotification(IntPtr hWnd, uint dwFlags);
@@ -83,6 +106,7 @@ namespace ColorControl
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static bool WinKeyDown = false;
private static UserNotificationState LastNotificationState;
public static Bitmap SubPixelShift(Bitmap bitmap)
{
@@ -428,11 +452,15 @@ namespace ColorControl
return null; // could also return string.Empty
}
public static List<string> GetDescriptions<T>() where T : IConvertible
public static List<string> GetDescriptions<T>(int value = -1, int fromValue = 0) where T : IConvertible
{
var list = new List<string>();
foreach (var enumValue in Enum.GetValues(typeof(T)))
{
if ((int)enumValue < fromValue || value >= 0 && ((int)enumValue & value) == 0)
{
continue;
}
list.Add(((T)enumValue).GetDescription());
}
return list;
@@ -579,6 +607,58 @@ namespace ColorControl
}
}
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)
{
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 string FormatKeyboardShortcut(KeyEventArgs keyEvent)
{
var pressedModifiers = keyEvent.Modifiers;
@@ -674,7 +754,14 @@ namespace ColorControl
item.Text = values[0];
for (var i = 1; i < values.Count; i++)
{
item.SubItems[i].Text = values[i];
if (item.SubItems.Count == i)
{
item.SubItems.Add(values[i]);
}
else
{
item.SubItems[i].Text = values[i];
}
}
}
}
@@ -743,18 +830,34 @@ namespace ColorControl
}
}
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;
}
tagRECT rect = new tagRECT();
IntPtr hWnd = (IntPtr)NativeMethods.GetForegroundWindow();
var hWnd = NativeMethods.GetForegroundWindow();
NativeMethods.GetWindowRect(hWnd, out rect);
NativeMethods.GetWindowRect(hWnd, out var rect);
/* in case you want the process name:
uint procId = 0;
@@ -763,19 +866,76 @@ namespace ColorControl
Console.WriteLine(proc.ProcessName);
*/
return screen.Bounds.Width <= (rect.right - rect.left) && screen.Bounds.Height <= (rect.bottom - rect.top);
}
if (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)
{
Console.WriteLine("Fullscreen!");
return true;
screen = Screen.PrimaryScreen;
}
else
var hWnd = NativeMethods.GetForegroundWindow();
if (hWnd == IntPtr.Zero)
{
Console.WriteLine("Nope, :-(");
return false;
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 ParseWords(List<string> target, string text)
{
target.Clear();
if (string.IsNullOrEmpty(text))
{
return;
}
var words = text.Split(',');
for (var i = 0; i < words.Length; i++)
{
var word = words[i];
if (word.IndexOf("(") > -1)
{
words[i] = word.Trim();
}
else
{
words[i] = word.Replace(" ", string.Empty);
}
}
target.AddRange(words);
}
}
public class EnumComboBoxItem
{
public string Name;
public int Id;
public override string ToString()
{
return Name;
}
}
+26
View File
@@ -6,10 +6,12 @@ namespace LgTv
public class ClientKeyStore
{
private string ip;
public ClientKeyStore(string ip)
{
this.ip = ip;
}
public string GetClientKey()
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
@@ -19,10 +21,34 @@ namespace LgTv
}
return null;
}
public void SaveClientKey(string key)
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
File.WriteAllText(filename, key);
}
public bool HasValidHandShake(string expectedHandShake)
{
var handShake = GetHandShake();
return handShake?.Equals(expectedHandShake) ?? false;
}
public string GetHandShake()
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
if (File.Exists(filename))
{
return File.ReadAllText(filename);
}
return null;
}
public void SaveHandShake(string data)
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
File.WriteAllText(filename, data);
}
}
}
+58 -11
View File
@@ -34,15 +34,31 @@ namespace LgTv
high
}
public enum LowToAuto
{
low,
medium,
high,
auto
}
public enum OffToOn
{
off,
on
}
public enum OffToAuto2
{
off,
on,
auto
}
public enum OffToAuto
{
off,
on,
low,
medium,
high,
@@ -67,6 +83,13 @@ namespace LgTv
screen_off
}
public enum DynamicTonemapping
{
on,
off,
HGIG,
}
public enum TruMotionMode
{
off,
@@ -86,15 +109,9 @@ namespace LgTv
private List<App> _appList;
private string _ip;
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\",\"READ_SETTINGS\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
public Task ControlButton(object oK)
{
// throw new NotImplementedException();
return null;
}
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\",\"READ_SETTINGS\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private string _currentPairKey;
private string webSocketUri;
@@ -133,7 +150,7 @@ namespace LgTv
public async Task MakeHandShake()
{
_currentPairKey = _keyStore.GetClientKey();
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0'))
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(BEFORE_PAIR_HAND_SHAKE))
{
var key = AFTER_PAIR_HAND_SHAKE.Replace("CLIENTKEYGOESHERE", _currentPairKey);
var conn = await _connection.SendCommandAsync(key);
@@ -143,6 +160,7 @@ namespace LgTv
dynamic result = await _connection.SendCommandAsync(BEFORE_PAIR_HAND_SHAKE);
_keyStore.SaveClientKey(result.clientKey);
_keyStore.SaveHandShake(BEFORE_PAIR_HAND_SHAKE);
}
@@ -346,6 +364,22 @@ namespace LgTv
await _connection.SubscribeAsync(requestMessage, callback);
}
public async Task SubscribePictureSettings(Func<dynamic, bool> callback, dynamic payload = null)
{
if (payload == null)
{
var keys = @"[ ""contrast"", ""backlight"" ]";
var json = @"{ ""category"": ""picture"", ""keys"": " + keys + @", ""subscribe"": true }";
payload = JObject.Parse(json);
}
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload, "subscribe");
await _connection.SubscribeAsync(requestMessage, callback);
}
public async Task<IEnumerable<App>> GetLaunchPoints()
{
_appList = new List<App>();
@@ -436,11 +470,24 @@ namespace LgTv
return (string)response.sessionId;
}
public async Task SetSystemSettings(string key, string value, string category = "picture")
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 lunauri = "luna://com.webos.settingsservice/setSystemSettings";
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": """ + value + @""" } }";
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" } }";
var @params = JObject.Parse(json);
+71 -35
View File
@@ -5,9 +5,9 @@ using System.Threading.Tasks;
using Newtonsoft.Json;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.Web;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using ColorControl;
namespace LgTv
{
@@ -45,30 +45,29 @@ namespace LgTv
_connection.MessageReceived += Connection_MessageReceived;
_connection.Closed += Connection_Closed;
var cancellationTokenSource = new CancellationTokenSource();
var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token);
cancellationTokenSource.CancelAfter(5000);
var result = await connectTask.ContinueWith((antecedent) =>
using (var cancellationTokenSource = new CancellationTokenSource(5000))
{
if (antecedent.Status == TaskStatus.RanToCompletion)
var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token);
var result = await connectTask.ContinueWith((antecedent) =>
{
// connectTask ran to completion, so we know that the MessageWebSocket is connected.
// Add additional code here to use the MessageWebSocket.
IsConnected?.Invoke(true);
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);
_messageWriter = new DataWriter(_connection.OutputStream);
return true;
}
else
{
// connectTask timed out, or faulted.
return false;
}
});
_messageWriter = new DataWriter(_connection.OutputStream);
return true;
}
else
{
// connectTask timed out, or faulted.
return false;
}
});
return result;
return result;
}
}
catch (Exception e)
{
@@ -85,12 +84,22 @@ namespace LgTv
}
}
public async void SendMessageAsync(string message)
public async Task SendMessageAsync(string message)
{
try
{
_messageWriter.WriteString(message);
await _messageWriter.StoreAsync();
using (var cancellationTokenSource = new CancellationTokenSource(5000))
{
await _messageWriter.StoreAsync().AsTask(cancellationTokenSource.Token).ContinueWith((antecedent) =>
{
if (antecedent.Status != TaskStatus.RanToCompletion)
{
throw new Exception("SendMessageAsync cancelled due to timeout");
}
});
}
}
catch (Exception e)
{
@@ -100,24 +109,26 @@ namespace LgTv
}
}
public Task<dynamic> SendCommandAsync(string message)
public async Task<dynamic> SendCommandAsync(string message)
{
var obj = JsonConvert.DeserializeObject<dynamic>(message);
return SendCommandAsync((string)obj.id, message);
return await SendCommandAsync((string)obj.id, message);
}
private Task<dynamic> SendCommandAsync(string id, string message)
private async Task<dynamic> SendCommandAsync(string id, string message)
{
try
{
var taskSource = new TaskCompletionSource<dynamic>();
_tokens.TryAdd(id, taskSource);
SendMessageAsync(message);
await SendMessageAsync(message);
if (ConnectionClosed)
{
throw new Exception("Connection closed");
}
return taskSource.Task;
var result = await taskSource.Task.ConfigureAwait(false);
return result;
}
catch (Exception e)
{
@@ -125,7 +136,7 @@ namespace LgTv
}
}
public Task<dynamic> SendCommandAsync(RequestMessage message)
public async Task<dynamic> SendCommandAsync(RequestMessage message)
{
var rawMessage = new RawRequestMessage(message, ++_commandCount);
var serialized = JsonConvert.SerializeObject(rawMessage, new JsonSerializerSettings()
@@ -133,10 +144,10 @@ namespace LgTv
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
return SendCommandAsync(rawMessage.Id, serialized);
return await SendCommandAsync(rawMessage.Id, serialized);
}
public Task<dynamic> SubscribeAsync(RequestMessage message, Func<dynamic, bool> callback)
public async Task<dynamic> SubscribeAsync(RequestMessage message, Func<dynamic, bool> callback)
{
var rawMessage = new RawRequestMessage(message, ++_commandCount);
var serialized = JsonConvert.SerializeObject(rawMessage, new JsonSerializerSettings()
@@ -147,7 +158,7 @@ namespace LgTv
_callbacks.TryAdd(rawMessage.Id, callback);
return SendCommandAsync(rawMessage.Id, serialized);
return await SendCommandAsync(rawMessage.Id, serialized);
}
private void Connection_Closed(IWebSocket sender, WebSocketClosedEventArgs args)
@@ -179,8 +190,7 @@ namespace LgTv
}
}
else
if (_tokens.TryGetValue(id, out taskCompletion))
else if (_tokens.TryGetValue(id, out taskCompletion))
{
if (id == "register_0") return;
if (obj.type == "error")
@@ -203,11 +213,37 @@ namespace LgTv
catch (Exception ex)
{
var status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.Message}");
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.ToLogString()}");
SetExceptionOnAllTokens(ex);
ConnectionClosed = true;
_messageWriter?.Dispose();
}
}
private void SetExceptionOnAllTokens(Exception ex)
{
//Logger.Debug($"Tokens: {_tokens.Count}");
foreach (var taskCompletion in _tokens.Values)
{
try
{
if (taskCompletion.Task?.Status == TaskStatus.Running)
{
taskCompletion.SetException(new Exception(ex.Message));
}
}
catch (Exception ex2)
{
Logger.Error("SetExceptionOnAllTokens: " + ex2.Message);
}
}
_tokens.Clear();
}
public void Close()
{
_connection?.Close(1, "");
+4 -4
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Native" version="0.1.0" 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.9" targetFramework="net472" />
<package id="NStandard" version="0.6.12.5" 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.0.7.6" 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" />