mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 00:22:18 -05:00
add triggers, fixed some bugs
This commit is contained in:
1 parent
90ca095954
commit
92f4ab8ef5
18 files changed
+1688
-271
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Generated
+130
-13
@@ -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
@@ -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(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in new issue
Block a user