mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-21 00:01:48 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1800b8d50 | ||
|
|
7779533c1e | ||
|
|
74cefc00f7 | ||
|
|
a8d33b6a30 | ||
|
|
f83869da04 | ||
|
|
815710c081 | ||
|
|
805c516371 | ||
|
|
b85d929890 | ||
|
|
dd9ce932fc | ||
|
|
3667243fc7 | ||
|
|
92f4ab8ef5 | ||
|
|
90ca095954 | ||
|
|
fa63d8dc4f | ||
|
|
fc134b146a | ||
|
|
3abf106496 | ||
|
|
a17534abcb |
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;
|
||||
|
||||
@@ -13,18 +13,11 @@ namespace ColorControl
|
||||
|
||||
private ADLDisplayInfo _currentDisplay;
|
||||
|
||||
private AmdPreset _lastAppliedPreset;
|
||||
|
||||
public AmdService(string dataPath) : base(dataPath)
|
||||
public AmdService(string dataPath) : base(dataPath, "AmdPresets.json")
|
||||
{
|
||||
LoadPresets();
|
||||
}
|
||||
|
||||
public AmdPreset GetLastAppliedPreset()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
public static bool ExecutePresetAsync(string idOrName)
|
||||
{
|
||||
try
|
||||
@@ -47,20 +40,9 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
protected override List<AmdPreset> GetDefaultPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, "AmdPresets.json");
|
||||
|
||||
if (File.Exists(_presetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<AmdPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presets = AmdPreset.GetDefaultPresets();
|
||||
}
|
||||
return AmdPreset.GetDefaultPresets();
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
@@ -191,6 +173,8 @@ namespace ColorControl
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
|
||||
@@ -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.1.0.0</ApplicationVersion>
|
||||
<ApplicationVersion>4.5.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>
|
||||
@@ -141,6 +141,9 @@
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
@@ -177,6 +180,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>
|
||||
@@ -238,8 +242,10 @@
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Resources\LG_register.json" />
|
||||
<EmbeddedResource Include="Resources\RC_coords.txt" />
|
||||
<None Include="Resources\LG_remote.jpg" />
|
||||
<None Include="ColorControl_1_TemporaryKey.pfx" />
|
||||
<EmbeddedResource Include="Resources\LG_remote.jpg" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Script.Serialization;
|
||||
@@ -22,33 +21,7 @@ namespace ColorControl
|
||||
|
||||
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
|
||||
{
|
||||
var format = ColorDataFormat.RGB;
|
||||
var dynamicRange = ColorDataDynamicRange.VESA;
|
||||
var colorDepth = ColorDataDepth.BPC8;
|
||||
var colorimetry = ColorDataColorimetry.Auto;
|
||||
var selectionPolicy = ColorDataSelectionPolicy.User;
|
||||
object value;
|
||||
if (dictionary.TryGetValue("ColorFormat", out value))
|
||||
{
|
||||
format = (ColorDataFormat)Enum.ToObject(typeof(ColorDataFormat), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("ColorDepth", out value))
|
||||
{
|
||||
colorDepth = (ColorDataDepth)Enum.ToObject(typeof(ColorDataDepth), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("Colorimetry", out value))
|
||||
{
|
||||
colorimetry = (ColorDataColorimetry)Enum.ToObject(typeof(ColorDataColorimetry), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("DynamicRange", out value))
|
||||
{
|
||||
dynamicRange = (ColorDataDynamicRange)Enum.ToObject(typeof(ColorDataDynamicRange), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out value))
|
||||
{
|
||||
selectionPolicy = (ColorDataSelectionPolicy)Enum.ToObject(typeof(ColorDataSelectionPolicy), value);
|
||||
}
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy, desktopColorDepth: ColorDataDesktopDepth.Default);
|
||||
return NvPreset.GenerateColorData(dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
public bool MinimizeToTray { get; set; }
|
||||
|
||||
public bool CheckForUpdates { get; set; }
|
||||
|
||||
public int DisplaySettingsDelay { get; set; }
|
||||
|
||||
public string ScreenSaverShortcut { get; set; }
|
||||
@@ -19,6 +21,8 @@
|
||||
|
||||
public int AmdPresetId_ApplyOnStartup { get; set; }
|
||||
|
||||
public bool FixChromeFonts { get; set; }
|
||||
|
||||
public Config()
|
||||
{
|
||||
DisplaySettingsDelay = 1000;
|
||||
@@ -26,6 +30,8 @@
|
||||
FormWidth = 900;
|
||||
FormHeight = 600;
|
||||
MinimizeToTray = true;
|
||||
CheckForUpdates = true;
|
||||
FixChromeFonts = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace ColorControl
|
||||
/// <param name="environmentStackTrace">Environment stack trace, for pulling additional stack frames.</param>
|
||||
public static string ToLogString(this Exception exception, string environmentStackTrace = "")
|
||||
{
|
||||
List<string> environmentStackTraceLines = ExceptionExtensions.GetUserStackTraceLines(environmentStackTrace);
|
||||
List<string> environmentStackTraceLines = GetUserStackTraceLines(environmentStackTrace);
|
||||
if (environmentStackTraceLines.Any())
|
||||
{
|
||||
environmentStackTraceLines.RemoveAt(0);
|
||||
@@ -29,7 +29,14 @@ namespace ColorControl
|
||||
|
||||
string fullStackTrace = String.Join(Environment.NewLine, stackTraceLines);
|
||||
|
||||
string logMessage = exception.Message + Environment.NewLine + fullStackTrace;
|
||||
string logMessage = exception.Message + Environment.NewLine;
|
||||
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
logMessage += $"Inner exception: {exception.InnerException.Message}{Environment.NewLine}";
|
||||
}
|
||||
|
||||
logMessage += fullStackTrace;
|
||||
return logMessage;
|
||||
}
|
||||
|
||||
@@ -51,7 +58,7 @@ namespace ColorControl
|
||||
List<string> outputList = new List<string>();
|
||||
Regex regex = new Regex(@"([^\)]*\)) in (.*):([A-z]*) (\d)*$");
|
||||
|
||||
List<string> stackTraceLines = ExceptionExtensions.GetStackTraceLines(fullStackTrace);
|
||||
List<string> stackTraceLines = GetStackTraceLines(fullStackTrace);
|
||||
foreach (string stackTraceLine in stackTraceLines)
|
||||
{
|
||||
if (!regex.IsMatch(stackTraceLine))
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ColorControl
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public GraphicsService(string dataPath) : base(dataPath)
|
||||
public GraphicsService(string dataPath, string presetsBaseFilename) : base(dataPath, presetsBaseFilename)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
+354
-23
@@ -11,7 +11,41 @@ namespace ColorControl
|
||||
{
|
||||
class LgDevice
|
||||
{
|
||||
public class InvokableAction
|
||||
{
|
||||
public Func<Dictionary<string, object>, bool> Function { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Type EnumType { get; set; }
|
||||
public decimal MinValue { get; set; }
|
||||
public decimal MaxValue { get; set; }
|
||||
public string Category { get; set; }
|
||||
}
|
||||
|
||||
public class LgDevicePictureSettings
|
||||
{
|
||||
public int Backlight { get; set; }
|
||||
public int Contrast { get; set; }
|
||||
}
|
||||
|
||||
public enum PowerState
|
||||
{
|
||||
Unknown,
|
||||
Active,
|
||||
Power_Off,
|
||||
Suspend,
|
||||
Active_Standby,
|
||||
Screen_Off
|
||||
}
|
||||
|
||||
public enum PowerOffSource
|
||||
{
|
||||
Unknown,
|
||||
App,
|
||||
Manually
|
||||
}
|
||||
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
public static Func<string, string[], bool> ExternalServiceHandler;
|
||||
|
||||
public string Name { get; private set; }
|
||||
public string IpAddress { get; private set; }
|
||||
@@ -26,51 +60,228 @@ namespace ColorControl
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public PowerState CurrentState { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private LgTvApi _lgTvApi;
|
||||
[JsonIgnore]
|
||||
private bool _justWokeUp;
|
||||
[JsonIgnore]
|
||||
public PowerOffSource PoweredOffBy { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private Dictionary<string, Func<bool>> _invokableActions = new Dictionary<string, Func<bool>>();
|
||||
public bool PoweredOffViaApp { get; private set; }
|
||||
[JsonIgnore]
|
||||
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;
|
||||
|
||||
_invokableActions.Add("WOL", new Func<bool>(Wake));
|
||||
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);
|
||||
AddGenericPictureAction("color", minValue: 0, maxValue: 100);
|
||||
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");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI2", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI3", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("uhdDeepColorHDMI4", typeof(OffToOn), category: "other");
|
||||
//AddGenericPictureAction("hdmiPcMode", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI1", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI2", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI3", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("gameOptimizationHDMI4", typeof(OffToOn), category: "other");
|
||||
//AddGenericPictureAction("freesyncOLEDHDMI4", typeof(OffToOn), category: "other");
|
||||
//AddGenericPictureAction("freesyncSupport", typeof(OffToOn), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi1", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi2", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi3", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi4", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("adjustingLuminance", minValue: -50, maxValue: 50);
|
||||
AddInvokableAction("turnScreenOff", new Func<Dictionary<string, object>, bool>(TurnScreenOffAction));
|
||||
AddInvokableAction("turnScreenOn", new Func<Dictionary<string, object>, bool>(TurnScreenOnAction));
|
||||
}
|
||||
|
||||
private void AddInvokableAction(string name, Func<Dictionary<string, object>, bool> function)
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
Name = name,
|
||||
Function = function
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
private void AddGenericPictureAction(string name, Type type = null, decimal minValue = 0, decimal maxValue = 0, string category = "picture")
|
||||
{
|
||||
var action = new InvokableAction
|
||||
{
|
||||
Name = name,
|
||||
Function = new Func<Dictionary<string, object>, bool>(GenericPictureAction),
|
||||
EnumType = type,
|
||||
MinValue = minValue,
|
||||
MaxValue = maxValue,
|
||||
Category = category
|
||||
};
|
||||
|
||||
_invokableActions.Add(action);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: ")) + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $" ({IpAddress})" : string.Empty);
|
||||
//return (IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: ")) + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $" ({IpAddress})" : string.Empty);
|
||||
return $"{(IsDummy ? string.Empty : (IsCustom ? "Custom: " : "Auto detect: "))}{Name}{(!string.IsNullOrEmpty(IpAddress) ? ", " + IpAddress : string.Empty)}";
|
||||
}
|
||||
|
||||
public async Task<bool> Connect(int retries = 3)
|
||||
{
|
||||
var locked = _connectSemaphore.CurrentCount == 0;
|
||||
await _connectSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
DisposeConnection();
|
||||
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries);
|
||||
if (locked && _lgTvApi != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//Test();
|
||||
//_lgTvApi.Test3();
|
||||
//_lgTvApi.SetSystemSettings();
|
||||
return _lgTvApi != null;
|
||||
try
|
||||
{
|
||||
DisposeConnection();
|
||||
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries);
|
||||
|
||||
//Test();
|
||||
//_lgTvApi.Test3();
|
||||
if (_lgTvApi != null)
|
||||
{
|
||||
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.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");
|
||||
}
|
||||
return _lgTvApi != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error($"Error while connecting to {IpAddress}: {logMessage}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
finally
|
||||
{
|
||||
string logMessage = ex.ToLogString(Environment.StackTrace);
|
||||
Logger.Error($"Error while connecting to {IpAddress}: {logMessage}");
|
||||
return false;
|
||||
_connectSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool VolumeChanged(dynamic payload)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PictureSettingsChanged(dynamic payload)
|
||||
{
|
||||
if (payload.settings != null)
|
||||
{
|
||||
var settings = payload.settings;
|
||||
|
||||
Logger.Debug($"PictureSettingsChanged: {JsonConvert.SerializeObject(settings)}");
|
||||
|
||||
if (settings.backlight != null)
|
||||
{
|
||||
PictureSettings.Backlight = Utils.ParseDynamicAsInt(settings.backlight, PictureSettings.Backlight);
|
||||
}
|
||||
if (settings.contrast != null)
|
||||
{
|
||||
PictureSettings.Contrast = Utils.ParseDynamicAsInt(settings.contrast, PictureSettings.Contrast);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PowerStateChanged(dynamic payload)
|
||||
{
|
||||
Logger.Debug($"[{Name}] Power state change: {JsonConvert.SerializeObject(payload)}");
|
||||
|
||||
var state = ((string)payload.state).Replace(' ', '_');
|
||||
|
||||
PowerState newState;
|
||||
if (Enum.TryParse(state, out newState))
|
||||
{
|
||||
CurrentState = newState;
|
||||
|
||||
if (CurrentState == PowerState.Active)
|
||||
{
|
||||
if (payload.processing == null && (DateTimeOffset.Now - _poweredOffViaAppDateTime).TotalMilliseconds > 500)
|
||||
{
|
||||
PoweredOffViaApp = false;
|
||||
PoweredOffBy = PowerOffSource.Unknown;
|
||||
_poweredOffViaAppDateTime = DateTimeOffset.MinValue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PoweredOffBy = PoweredOffViaApp ? PowerOffSource.App : PowerOffSource.Manually;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentState = PowerState.Unknown;
|
||||
Logger.Warn($"Unknown power state: {state}");
|
||||
}
|
||||
|
||||
Logger.Debug($"PoweredOffBy: {PoweredOffBy}, PoweredOffViaApp: {PoweredOffViaApp}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
return !_lgTvApi?.ConnectionClosed ?? false;
|
||||
@@ -84,7 +295,8 @@ namespace ColorControl
|
||||
_lgTvApi = null;
|
||||
}
|
||||
}
|
||||
public async Task<bool> ExecutePreset(LgPreset preset, bool reconnect = false)
|
||||
|
||||
public async Task<bool> ExecutePreset(LgPreset preset, bool reconnect, LgServiceConfig config)
|
||||
{
|
||||
var hasApp = !string.IsNullOrEmpty(preset.appId);
|
||||
|
||||
@@ -110,7 +322,18 @@ namespace ColorControl
|
||||
{
|
||||
try
|
||||
{
|
||||
await _lgTvApi.LaunchApp(preset.appId);
|
||||
dynamic @params = null;
|
||||
if (config.AllowFirmwareDowngrade && preset.appId.Equals("com.webos.app.softwareupdate"))
|
||||
{
|
||||
@params = new { mode = "user", flagUpdate = true };
|
||||
}
|
||||
|
||||
if (preset.appId.Equals("com.webos.app.factorywin"))
|
||||
{
|
||||
@params = new { id = "executeFactory", irKey = "inStart" };
|
||||
}
|
||||
|
||||
await _lgTvApi.LaunchApp(preset.appId, @params);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -167,23 +390,73 @@ namespace ColorControl
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
var key = keySpec[0].ToUpper();
|
||||
if (_invokableActions.ContainsKey(key))
|
||||
var index = key.IndexOf("(");
|
||||
string[] parameters = null;
|
||||
if (index > -1)
|
||||
{
|
||||
continue;
|
||||
var keyValue = keySpec[0].Split('(');
|
||||
key = keyValue[0];
|
||||
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
|
||||
}
|
||||
|
||||
var delay = 0;
|
||||
if (keySpec.Length == 2)
|
||||
{
|
||||
SendKey(mouse, key, int.Parse(keySpec[1]));
|
||||
delay = Utils.ParseInt(keySpec[1]);
|
||||
}
|
||||
else
|
||||
|
||||
var executeKey = true;
|
||||
var action = _invokableActions.FirstOrDefault(a => a.Name.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
if (action != null)
|
||||
{
|
||||
if (!key.Equals("WOL"))
|
||||
{
|
||||
ExecuteAction(action, parameters);
|
||||
}
|
||||
|
||||
executeKey = false;
|
||||
}
|
||||
if (ExternalServiceHandler != null && parameters != null)
|
||||
{
|
||||
if (ExternalServiceHandler(key, parameters))
|
||||
{
|
||||
executeKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (executeKey)
|
||||
{
|
||||
SendKey(mouse, key);
|
||||
delay = delay == 0 ? 180 : delay;
|
||||
}
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key, int delay = 180)
|
||||
private void ExecuteAction(InvokableAction action, string[] parameters)
|
||||
{
|
||||
var function = action.Function;
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
var keyValues = new Dictionary<string, object> {
|
||||
{ "name", action.Name },
|
||||
{ "value", parameters },
|
||||
{ "category", action.Category }
|
||||
};
|
||||
|
||||
function(keyValues);
|
||||
return;
|
||||
}
|
||||
|
||||
function(null);
|
||||
}
|
||||
|
||||
private void SendKey(LgWebOsMouseService mouse, string key)
|
||||
{
|
||||
if (key.Length == 1 && int.TryParse(key, out _))
|
||||
{
|
||||
@@ -191,7 +464,6 @@ namespace ColorControl
|
||||
}
|
||||
var button = (ButtonType)Enum.Parse(typeof(ButtonType), key);
|
||||
mouse.SendButton(button);
|
||||
Thread.Sleep(delay);
|
||||
}
|
||||
|
||||
public async Task<LgWebOsMouseService> GetMouseAsync()
|
||||
@@ -212,12 +484,16 @@ namespace ColorControl
|
||||
|
||||
internal async Task<bool> PowerOff()
|
||||
{
|
||||
if (!await Connected(true))
|
||||
if (!await Connected(true) || CurrentState != PowerState.Active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PoweredOffViaApp = true;
|
||||
_poweredOffViaAppDateTime = DateTimeOffset.Now;
|
||||
|
||||
await _lgTvApi.TurnOff();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -325,7 +601,7 @@ namespace ColorControl
|
||||
return result;
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<bool>> GetInvokableActions()
|
||||
public List<InvokableAction> GetInvokableActions()
|
||||
{
|
||||
return _invokableActions;
|
||||
}
|
||||
@@ -367,6 +643,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());
|
||||
@@ -381,5 +663,54 @@ namespace ColorControl
|
||||
{
|
||||
await _lgTvApi.SetConfig(key, value);
|
||||
}
|
||||
internal async Task SetSystemSettings(string name, string value)
|
||||
{
|
||||
await _lgTvApi.SetSystemSettings(name, value);
|
||||
}
|
||||
|
||||
internal async Task<dynamic> GetPictureSettings()
|
||||
{
|
||||
//var keys = new[] { "backlight", "brightness", "contrast", "color", "pictureMode", "colorGamut", "dynamicContrast", "peakBrightness", "smoothGradation", "energySaving", "motionProOLED" };
|
||||
var keys = new[] { "backlight", "brightness", "contrast", "color" };
|
||||
|
||||
return await _lgTvApi.GetSystemSettings("picture", keys);
|
||||
}
|
||||
|
||||
private bool WakeAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
return Wake();
|
||||
}
|
||||
|
||||
private bool TurnScreenOffAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
var task = _lgTvApi.TurnScreenOff();
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TurnScreenOnAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
var task = _lgTvApi.TurnScreenOn();
|
||||
Utils.WaitForTask(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool GenericPictureAction(Dictionary<string, object> parameters)
|
||||
{
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+225
-63
@@ -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,8 +64,9 @@ namespace ColorControl
|
||||
private int _poweredOffByScreenSaverProcessId;
|
||||
private Task _monitorTask;
|
||||
private int _monitorTaskCounter;
|
||||
|
||||
public LgService(string dataPath, bool allowPowerOn) : base(dataPath)
|
||||
private LgPreset _lastTriggeredPreset;
|
||||
|
||||
public LgService(string dataPath, bool allowPowerOn) : base(dataPath, "LgPresets.json")
|
||||
{
|
||||
_allowPowerOn = allowPowerOn;
|
||||
|
||||
@@ -115,33 +128,19 @@ namespace ColorControl
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
protected override List<LgPreset> GetDefaultPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, "LgPresets.json");
|
||||
var toCopy = Path.Combine(Directory.GetCurrentDirectory(), "LgPresets.json");
|
||||
if (!File.Exists(_presetsFilename) && File.Exists(toCopy))
|
||||
List<LgPreset> presets = null;
|
||||
|
||||
var defaultPresetsFileName = Path.Combine(Directory.GetCurrentDirectory(), "LgPresets.json");
|
||||
if (File.Exists(defaultPresetsFileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(toCopy, _presetsFilename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Error while copying {toCopy} to {_presetsFilename}: {e.Message}");
|
||||
}
|
||||
var json = File.ReadAllText(defaultPresetsFileName);
|
||||
|
||||
presets = _JsonDeserializer.Deserialize<List<LgPreset>>(json);
|
||||
}
|
||||
|
||||
if (File.Exists(_presetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
_presets = _JsonSerializer.Deserialize<List<LgPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presets = new List<LgPreset>();
|
||||
}
|
||||
return presets;
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
@@ -265,7 +264,7 @@ namespace ColorControl
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
Config.PowerOnAfterStartup = Devices.Any(d => d.PowerOnAfterStartup);
|
||||
Config.PowerOnAfterStartup = Devices?.Any(d => d.PowerOnAfterStartup) ?? false;
|
||||
|
||||
var json = JsonConvert.SerializeObject(Config);
|
||||
File.WriteAllText(_configFilename, json);
|
||||
@@ -352,7 +351,11 @@ namespace ColorControl
|
||||
return false;
|
||||
}
|
||||
|
||||
return await device.ExecutePreset(preset, reconnect);
|
||||
var result = await device.ExecutePreset(preset, reconnect, Config);
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public LgDevice GetPresetDevice(LgPreset preset)
|
||||
@@ -376,7 +379,7 @@ namespace ColorControl
|
||||
return;
|
||||
}
|
||||
|
||||
var apps = await SelectedDevice.GetApps();
|
||||
var apps = await SelectedDevice.GetApps(force);
|
||||
|
||||
if (apps.Any())
|
||||
{
|
||||
@@ -429,6 +432,12 @@ namespace ColorControl
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver);
|
||||
foreach (var device in wakeDevices)
|
||||
{
|
||||
if (device.PoweredOffBy == LgDevice.PowerOffSource.Manually)
|
||||
{
|
||||
Logger.Debug($"[{device.Name}]: device was manually powered off by user, not powering on");
|
||||
continue;
|
||||
}
|
||||
|
||||
device.DisposeConnection();
|
||||
var _ = device.WakeAndConnectWithRetries(Config.PowerOnRetries);
|
||||
}
|
||||
@@ -515,7 +524,7 @@ namespace ColorControl
|
||||
|
||||
private void MonitorProcesses()
|
||||
{
|
||||
var enableMonitoring = Devices.Any(d => d.PowerSwitchOnScreenSaver);
|
||||
var enableMonitoring = ShouldMonitorProcesses();
|
||||
if (enableMonitoring && _monitorTask == null)
|
||||
{
|
||||
_monitorTask = CheckProcesses();
|
||||
@@ -526,6 +535,11 @@ 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());
|
||||
@@ -533,12 +547,23 @@ namespace ColorControl
|
||||
var validCounter = _monitorTaskCounter;
|
||||
var lastProcessId = 0;
|
||||
|
||||
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())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_poweredOffByScreenSaver && !UserSessionInfo.UserLocalSession)
|
||||
{
|
||||
continue;
|
||||
@@ -562,64 +587,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;
|
||||
}
|
||||
|
||||
monitorContext.Devices = connectedDevices;
|
||||
monitorContext.RunningProcesses = processes;
|
||||
//if (lastProcesses != null)
|
||||
//{
|
||||
// monitorContext.StartedProcesses = processes.Where(p => !lastProcesses.Any(lp => lp.Id == p.Id)).ToArray();
|
||||
// monitorContext.StoppedProcesses = lastProcesses.Where(lp => !processes.Any(p => p.Id == lp.Id)).ToArray();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// monitorContext.StartedProcesses = null;
|
||||
// monitorContext.StoppedProcesses = null;
|
||||
//}
|
||||
|
||||
//if (monitorContext.StartedProcesses?.Any() ?? false)
|
||||
//{
|
||||
// Logger.Debug("Started processes: " + string.Join(", ", monitorContext.StartedProcesses.Select(p => p.ProcessName)));
|
||||
//}
|
||||
//lastProcesses = processes;
|
||||
|
||||
var process = processes.FirstOrDefault(p => p.ProcessName.ToLowerInvariant().EndsWith(".scr"));
|
||||
|
||||
if (process == null)
|
||||
if (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)
|
||||
@@ -629,6 +702,95 @@ 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 (_lastTriggeredPreset != toApplyPreset)
|
||||
{
|
||||
await ApplyPreset(toApplyPreset);
|
||||
|
||||
_lastTriggeredPreset = toApplyPreset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddCustomDevice(LgDevice device)
|
||||
{
|
||||
Devices.Add(device);
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace ColorControl
|
||||
|
||||
public bool ShowRemoteControl { get; set; }
|
||||
|
||||
public bool AllowFirmwareDowngrade { get; set; }
|
||||
|
||||
public LgServiceConfig()
|
||||
{
|
||||
PowerOnDelayAfterResume = 5000;
|
||||
|
||||
Generated
+226
-54
@@ -104,7 +104,22 @@
|
||||
this.lvAmdPresets = new System.Windows.Forms.ListView();
|
||||
this.tabLG = new System.Windows.Forms.TabPage();
|
||||
this.scLgController = new System.Windows.Forms.SplitContainer();
|
||||
this.btnLgPresetEditTriggerConditions = new System.Windows.Forms.Button();
|
||||
this.edtLgPresetTriggerConditions = new System.Windows.Forms.TextBox();
|
||||
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();
|
||||
this.miLgEnableMotionPro = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miLgDisableMotionPro = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miLgExpertSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuLgExpertBacklight = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.chkLgRemoteControlShow = new System.Windows.Forms.CheckBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.cbxLgDevices = new System.Windows.Forms.ComboBox();
|
||||
@@ -132,6 +147,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();
|
||||
@@ -161,6 +178,7 @@
|
||||
this.edtDelayDisplaySettings = new System.Windows.Forms.NumericUpDown();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.grpGeneralOptions = new System.Windows.Forms.GroupBox();
|
||||
this.chkCheckForUpdates = new System.Windows.Forms.CheckBox();
|
||||
this.chkMinimizeToSystemTray = new System.Windows.Forms.CheckBox();
|
||||
this.chkMinimizeOnClose = new System.Windows.Forms.CheckBox();
|
||||
this.chkStartMinimized = new System.Windows.Forms.CheckBox();
|
||||
@@ -176,12 +194,7 @@
|
||||
this.lbPlugins = new System.Windows.Forms.ListBox();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.lblInfo = new System.Windows.Forms.Label();
|
||||
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
|
||||
this.mnuLgExpert = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.mnuLgExpertBacklight = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuLgOLEDMotionPro = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miLgEnableMotionPro = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miLgDisableMotionPro = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.chkLgAllowDowngrade = new System.Windows.Forms.CheckBox();
|
||||
this.tcMain.SuspendLayout();
|
||||
this.tabNVIDIA.SuspendLayout();
|
||||
this.mnuNvPresets.SuspendLayout();
|
||||
@@ -191,6 +204,7 @@
|
||||
((System.ComponentModel.ISupportInitialize)(this.scLgController)).BeginInit();
|
||||
this.scLgController.Panel1.SuspendLayout();
|
||||
this.scLgController.SuspendLayout();
|
||||
this.mnuLgExpert.SuspendLayout();
|
||||
this.mnuLgButtons.SuspendLayout();
|
||||
this.tabOptions.SuspendLayout();
|
||||
this.grpNvidiaOptions.SuspendLayout();
|
||||
@@ -205,7 +219,6 @@
|
||||
this.tabInfo.SuspendLayout();
|
||||
this.grpNVIDIAInfo.SuspendLayout();
|
||||
this.groupBox3.SuspendLayout();
|
||||
this.mnuLgExpert.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tcMain
|
||||
@@ -939,6 +952,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);
|
||||
@@ -975,6 +997,96 @@
|
||||
this.scLgController.SplitterDistance = 767;
|
||||
this.scLgController.TabIndex = 43;
|
||||
//
|
||||
// btnLgPresetEditTriggerConditions
|
||||
//
|
||||
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);
|
||||
//
|
||||
// edtLgPresetTriggerConditions
|
||||
//
|
||||
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;
|
||||
//
|
||||
// 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;
|
||||
@@ -986,6 +1098,51 @@
|
||||
this.btnLgExpert.UseVisualStyleBackColor = true;
|
||||
this.btnLgExpert.Click += new System.EventHandler(this.btnLgExpert_Click);
|
||||
//
|
||||
// mnuLgExpert
|
||||
//
|
||||
this.mnuLgExpert.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.mnuLgExpert.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuLgOLEDMotionPro,
|
||||
this.miLgExpertSeparator1,
|
||||
this.mnuLgExpertBacklight});
|
||||
this.mnuLgExpert.Name = "mnuLgButtons";
|
||||
this.mnuLgExpert.Size = new System.Drawing.Size(321, 54);
|
||||
this.mnuLgExpert.Opening += new System.ComponentModel.CancelEventHandler(this.mnuLgExpert_Opening);
|
||||
//
|
||||
// mnuLgOLEDMotionPro
|
||||
//
|
||||
this.mnuLgOLEDMotionPro.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miLgEnableMotionPro,
|
||||
this.miLgDisableMotionPro});
|
||||
this.mnuLgOLEDMotionPro.Name = "mnuLgOLEDMotionPro";
|
||||
this.mnuLgOLEDMotionPro.Size = new System.Drawing.Size(320, 22);
|
||||
this.mnuLgOLEDMotionPro.Text = "Activate OLED Motion Pro (B9/C9/E9/W9 only)";
|
||||
//
|
||||
// miLgEnableMotionPro
|
||||
//
|
||||
this.miLgEnableMotionPro.Name = "miLgEnableMotionPro";
|
||||
this.miLgEnableMotionPro.Size = new System.Drawing.Size(207, 22);
|
||||
this.miLgEnableMotionPro.Text = "Enable OLED Motion Pro";
|
||||
this.miLgEnableMotionPro.Click += new System.EventHandler(this.miLgEnableMotionPro_Click);
|
||||
//
|
||||
// miLgDisableMotionPro
|
||||
//
|
||||
this.miLgDisableMotionPro.Name = "miLgDisableMotionPro";
|
||||
this.miLgDisableMotionPro.Size = new System.Drawing.Size(207, 22);
|
||||
this.miLgDisableMotionPro.Text = "Disable OLED Motion Pro";
|
||||
this.miLgDisableMotionPro.Click += new System.EventHandler(this.miLgDisableMotionPro_Click);
|
||||
//
|
||||
// miLgExpertSeparator1
|
||||
//
|
||||
this.miLgExpertSeparator1.Name = "miLgExpertSeparator1";
|
||||
this.miLgExpertSeparator1.Size = new System.Drawing.Size(317, 6);
|
||||
//
|
||||
// mnuLgExpertBacklight
|
||||
//
|
||||
this.mnuLgExpertBacklight.Name = "mnuLgExpertBacklight";
|
||||
this.mnuLgExpertBacklight.Size = new System.Drawing.Size(320, 22);
|
||||
this.mnuLgExpertBacklight.Text = "Backlight";
|
||||
//
|
||||
// chkLgRemoteControlShow
|
||||
//
|
||||
this.chkLgRemoteControlShow.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
@@ -1011,6 +1168,7 @@
|
||||
// cbxLgDevices
|
||||
//
|
||||
this.cbxLgDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.cbxLgDevices.DropDownWidth = 400;
|
||||
this.cbxLgDevices.FormattingEnabled = true;
|
||||
this.cbxLgDevices.Location = new System.Drawing.Point(86, 8);
|
||||
this.cbxLgDevices.Name = "cbxLgDevices";
|
||||
@@ -1038,6 +1196,7 @@
|
||||
this.cbxLgApps.Name = "cbxLgApps";
|
||||
this.cbxLgApps.Size = new System.Drawing.Size(200, 21);
|
||||
this.cbxLgApps.TabIndex = 26;
|
||||
this.cbxLgApps.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.cbxLgApps_KeyPress);
|
||||
//
|
||||
// lvLgPresets
|
||||
//
|
||||
@@ -1253,23 +1412,37 @@
|
||||
this.mnuLgButtons.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.mnuLgButtons.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuLgRcButtons,
|
||||
this.mnuLgActions});
|
||||
this.mnuLgActions,
|
||||
this.mnuLgNvPresets,
|
||||
this.mnuLgAmdPresets});
|
||||
this.mnuLgButtons.Name = "mnuLgButtons";
|
||||
this.mnuLgButtons.Size = new System.Drawing.Size(116, 48);
|
||||
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(115, 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(115, 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)));
|
||||
@@ -1438,7 +1611,7 @@
|
||||
this.grpMiscellaneousOptions.Controls.Add(this.edtBlankScreenSaverShortcut);
|
||||
this.grpMiscellaneousOptions.Controls.Add(this.lblFixChromeFontsDescription);
|
||||
this.grpMiscellaneousOptions.Controls.Add(this.chkFixChromeFonts);
|
||||
this.grpMiscellaneousOptions.Location = new System.Drawing.Point(6, 309);
|
||||
this.grpMiscellaneousOptions.Location = new System.Drawing.Point(6, 253);
|
||||
this.grpMiscellaneousOptions.Name = "grpMiscellaneousOptions";
|
||||
this.grpMiscellaneousOptions.Size = new System.Drawing.Size(400, 130);
|
||||
this.grpMiscellaneousOptions.TabIndex = 5;
|
||||
@@ -1496,14 +1669,15 @@
|
||||
//
|
||||
// grpLGOptions
|
||||
//
|
||||
this.grpLGOptions.Controls.Add(this.chkLgAllowDowngrade);
|
||||
this.grpLGOptions.Controls.Add(this.chkLgOldWolMechanism);
|
||||
this.grpLGOptions.Controls.Add(this.btnLGTestPower);
|
||||
this.grpLGOptions.Controls.Add(this.lblLgMaxPowerOnRetriesDescription);
|
||||
this.grpLGOptions.Controls.Add(this.edtLgMaxPowerOnRetries);
|
||||
this.grpLGOptions.Controls.Add(this.lblLgMaxPowerOnRetries);
|
||||
this.grpLGOptions.Location = new System.Drawing.Point(6, 196);
|
||||
this.grpLGOptions.Location = new System.Drawing.Point(6, 118);
|
||||
this.grpLGOptions.Name = "grpLGOptions";
|
||||
this.grpLGOptions.Size = new System.Drawing.Size(403, 107);
|
||||
this.grpLGOptions.Size = new System.Drawing.Size(403, 129);
|
||||
this.grpLGOptions.TabIndex = 4;
|
||||
this.grpLGOptions.TabStop = false;
|
||||
this.grpLGOptions.Text = "LG controller";
|
||||
@@ -1567,12 +1741,13 @@
|
||||
//
|
||||
this.grpHDROptions.Controls.Add(this.edtDelayDisplaySettings);
|
||||
this.grpHDROptions.Controls.Add(this.label6);
|
||||
this.grpHDROptions.Location = new System.Drawing.Point(6, 118);
|
||||
this.grpHDROptions.Location = new System.Drawing.Point(412, 316);
|
||||
this.grpHDROptions.Name = "grpHDROptions";
|
||||
this.grpHDROptions.Size = new System.Drawing.Size(400, 72);
|
||||
this.grpHDROptions.TabIndex = 3;
|
||||
this.grpHDROptions.TabStop = false;
|
||||
this.grpHDROptions.Text = "HDR";
|
||||
this.grpHDROptions.Visible = false;
|
||||
//
|
||||
// edtDelayDisplaySettings
|
||||
//
|
||||
@@ -1605,6 +1780,7 @@
|
||||
//
|
||||
// grpGeneralOptions
|
||||
//
|
||||
this.grpGeneralOptions.Controls.Add(this.chkCheckForUpdates);
|
||||
this.grpGeneralOptions.Controls.Add(this.chkMinimizeToSystemTray);
|
||||
this.grpGeneralOptions.Controls.Add(this.chkMinimizeOnClose);
|
||||
this.grpGeneralOptions.Controls.Add(this.chkStartMinimized);
|
||||
@@ -1616,6 +1792,17 @@
|
||||
this.grpGeneralOptions.TabStop = false;
|
||||
this.grpGeneralOptions.Text = "General";
|
||||
//
|
||||
// chkCheckForUpdates
|
||||
//
|
||||
this.chkCheckForUpdates.AutoSize = true;
|
||||
this.chkCheckForUpdates.Location = new System.Drawing.Point(204, 42);
|
||||
this.chkCheckForUpdates.Name = "chkCheckForUpdates";
|
||||
this.chkCheckForUpdates.Size = new System.Drawing.Size(177, 17);
|
||||
this.chkCheckForUpdates.TabIndex = 6;
|
||||
this.chkCheckForUpdates.Text = "Automatically check for updates";
|
||||
this.chkCheckForUpdates.UseVisualStyleBackColor = true;
|
||||
this.chkCheckForUpdates.CheckedChanged += new System.EventHandler(this.chkCheckForUpdates_CheckedChanged);
|
||||
//
|
||||
// chkMinimizeToSystemTray
|
||||
//
|
||||
this.chkMinimizeToSystemTray.AutoSize = true;
|
||||
@@ -1781,44 +1968,16 @@
|
||||
this.lblInfo.TabIndex = 1;
|
||||
this.lblInfo.Text = "Info";
|
||||
//
|
||||
// mnuLgExpert
|
||||
// chkLgAllowDowngrade
|
||||
//
|
||||
this.mnuLgExpert.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.mnuLgExpert.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuLgExpertBacklight,
|
||||
this.mnuLgOLEDMotionPro});
|
||||
this.mnuLgExpert.Name = "mnuLgButtons";
|
||||
this.mnuLgExpert.Size = new System.Drawing.Size(236, 70);
|
||||
this.mnuLgExpert.Opening += new System.ComponentModel.CancelEventHandler(this.mnuLgExpert_Opening);
|
||||
//
|
||||
// mnuLgExpertBacklight
|
||||
//
|
||||
this.mnuLgExpertBacklight.Name = "mnuLgExpertBacklight";
|
||||
this.mnuLgExpertBacklight.Size = new System.Drawing.Size(235, 22);
|
||||
this.mnuLgExpertBacklight.Text = "Backlight";
|
||||
//
|
||||
// mnuLgOLEDMotionPro
|
||||
//
|
||||
this.mnuLgOLEDMotionPro.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miLgEnableMotionPro,
|
||||
this.miLgDisableMotionPro});
|
||||
this.mnuLgOLEDMotionPro.Name = "mnuLgOLEDMotionPro";
|
||||
this.mnuLgOLEDMotionPro.Size = new System.Drawing.Size(235, 22);
|
||||
this.mnuLgOLEDMotionPro.Text = "OLED Motion Pro (B9/C9 only)";
|
||||
//
|
||||
// miLgEnableMotionPro
|
||||
//
|
||||
this.miLgEnableMotionPro.Name = "miLgEnableMotionPro";
|
||||
this.miLgEnableMotionPro.Size = new System.Drawing.Size(207, 22);
|
||||
this.miLgEnableMotionPro.Text = "Enable OLED Motion Pro";
|
||||
this.miLgEnableMotionPro.Click += new System.EventHandler(this.miLgEnableMotionPro_Click);
|
||||
//
|
||||
// miLgDisableMotionPro
|
||||
//
|
||||
this.miLgDisableMotionPro.Name = "miLgDisableMotionPro";
|
||||
this.miLgDisableMotionPro.Size = new System.Drawing.Size(207, 22);
|
||||
this.miLgDisableMotionPro.Text = "Disable OLED Motion Pro";
|
||||
this.miLgDisableMotionPro.Click += new System.EventHandler(this.miLgDisableMotionPro_Click);
|
||||
this.chkLgAllowDowngrade.AutoSize = true;
|
||||
this.chkLgAllowDowngrade.Location = new System.Drawing.Point(9, 97);
|
||||
this.chkLgAllowDowngrade.Name = "chkLgAllowDowngrade";
|
||||
this.chkLgAllowDowngrade.Size = new System.Drawing.Size(275, 17);
|
||||
this.chkLgAllowDowngrade.TabIndex = 8;
|
||||
this.chkLgAllowDowngrade.Text = "Allow firmware downgrade with Software Update-app";
|
||||
this.chkLgAllowDowngrade.UseVisualStyleBackColor = true;
|
||||
this.chkLgAllowDowngrade.CheckedChanged += new System.EventHandler(this.chkLgAllowDowngrade_CheckedChanged);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
@@ -1849,6 +2008,7 @@
|
||||
this.scLgController.Panel1.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.scLgController)).EndInit();
|
||||
this.scLgController.ResumeLayout(false);
|
||||
this.mnuLgExpert.ResumeLayout(false);
|
||||
this.mnuLgButtons.ResumeLayout(false);
|
||||
this.tabOptions.ResumeLayout(false);
|
||||
this.grpNvidiaOptions.ResumeLayout(false);
|
||||
@@ -1870,7 +2030,6 @@
|
||||
this.grpNVIDIAInfo.ResumeLayout(false);
|
||||
this.groupBox3.ResumeLayout(false);
|
||||
this.groupBox3.PerformLayout();
|
||||
this.mnuLgExpert.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
@@ -2022,13 +2181,26 @@
|
||||
private System.Windows.Forms.CheckedListBox clbLgPower;
|
||||
private System.Windows.Forms.Label lblLgError;
|
||||
private System.Windows.Forms.CheckBox chkLgRemoteControlShow;
|
||||
private System.ComponentModel.BackgroundWorker backgroundWorker1;
|
||||
private System.Windows.Forms.Button btnLgExpert;
|
||||
private System.Windows.Forms.ContextMenuStrip mnuLgExpert;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuLgExpertBacklight;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuLgOLEDMotionPro;
|
||||
private System.Windows.Forms.ToolStripMenuItem miLgEnableMotionPro;
|
||||
private System.Windows.Forms.ToolStripMenuItem miLgDisableMotionPro;
|
||||
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;
|
||||
private System.Windows.Forms.CheckBox chkCheckForUpdates;
|
||||
private System.Windows.Forms.CheckBox chkLgAllowDowngrade;
|
||||
}
|
||||
}
|
||||
|
||||
+432
-71
@@ -52,6 +52,10 @@ namespace ColorControl
|
||||
private StartUpParams StartUpParams { get; }
|
||||
|
||||
private AmdService _amdService;
|
||||
private bool _skipResize;
|
||||
private FileVersionInfo _currentVersionInfo;
|
||||
private bool _checkedForUpdates = false;
|
||||
private string _updateHtmlUrl;
|
||||
|
||||
public MainForm(AppContext appContext)
|
||||
{
|
||||
@@ -85,12 +89,18 @@ namespace ColorControl
|
||||
};
|
||||
_trayIcon.MouseDoubleClick += trayIcon_MouseDoubleClick;
|
||||
_trayIcon.ContextMenu.Popup += trayIconContextMenu_Popup;
|
||||
_trayIcon.BalloonTipClicked += trayIconBalloonTip_Clicked;
|
||||
|
||||
chkStartAfterLogin.Checked = Utils.TaskExists(TS_TASKNAME, true);
|
||||
|
||||
chkFixChromeFonts.Enabled = Utils.IsChromeInstalled();
|
||||
if (chkFixChromeFonts.Enabled)
|
||||
{
|
||||
var fixInstalled = Utils.IsChromeFixInstalled();
|
||||
if (_config.FixChromeFonts && !fixInstalled)
|
||||
{
|
||||
Utils.ExecuteElevated(StartUpParams.ActivateChromeFontFixParam);
|
||||
}
|
||||
chkFixChromeFonts.Checked = Utils.IsChromeFixInstalled();
|
||||
}
|
||||
|
||||
@@ -115,23 +125,33 @@ namespace ColorControl
|
||||
{
|
||||
try
|
||||
{
|
||||
//throw new Exception("bla");
|
||||
_nvService = new NvService(_dataDir);
|
||||
FillNvPresets();
|
||||
|
||||
_nvService.AfterApplyPreset += NvServiceAfterApplyPreset;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Logger.Error("Error initializing NvService: " + e.ToLogString());
|
||||
Logger.Debug("No NVIDIA device detected");
|
||||
Logger.Debug($"No NVIDIA device detected: {ex.Message}");
|
||||
tcMain.TabPages.Remove(tabNVIDIA);
|
||||
}
|
||||
}
|
||||
|
||||
private void NvServiceAfterApplyPreset(object sender, NvPreset preset)
|
||||
{
|
||||
UpdateDisplayInfoItems();
|
||||
}
|
||||
|
||||
private void InitAmdService()
|
||||
{
|
||||
try
|
||||
{
|
||||
_amdService = new AmdService(_dataDir);
|
||||
FillAmdPresets();
|
||||
|
||||
_amdService.AfterApplyPreset += AmdServiceAfterApplyPreset;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -141,6 +161,11 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
private void AmdServiceAfterApplyPreset(object sender, AmdPreset preset)
|
||||
{
|
||||
UpdateDisplayInfoItemsAmd();
|
||||
}
|
||||
|
||||
private void InitLgService()
|
||||
{
|
||||
try
|
||||
@@ -153,6 +178,7 @@ namespace ColorControl
|
||||
edtLgMaxPowerOnRetries.Value = _lgService.Config.PowerOnRetries;
|
||||
edtLgDeviceFilter.Text = _lgService.Config.DeviceSearchKey;
|
||||
chkLgOldWolMechanism.Checked = _lgService.Config.UseOldNpcapWol;
|
||||
chkLgAllowDowngrade.Checked = _lgService.Config.AllowFirmwareDowngrade;
|
||||
|
||||
var values = Enum.GetValues(typeof(ButtonType));
|
||||
foreach (var button in values)
|
||||
@@ -168,6 +194,8 @@ namespace ColorControl
|
||||
}
|
||||
|
||||
_lgService.InstallEventHandlers();
|
||||
|
||||
LgDevice.ExternalServiceHandler = HandleExternalServiceForLgDevice;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -177,15 +205,36 @@ namespace ColorControl
|
||||
|
||||
private void AfterLgServiceRefreshDevices()
|
||||
{
|
||||
FillLgDevices();
|
||||
|
||||
if (StartUpParams.ExecuteLgPreset)
|
||||
{
|
||||
var _ = _lgService.ApplyPreset(StartUpParams.LgPresetName);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleExternalServiceForLgDevice(string serviceName, string[] parameters)
|
||||
{
|
||||
if (string.IsNullOrEmpty(serviceName) || parameters.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_nvService != null && serviceName.Equals("NvPreset", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return _nvService.ApplyPreset(parameters[0]);
|
||||
}
|
||||
if (_amdService != null && serviceName.Equals("AmdPreset", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return _amdService.ApplyPreset(parameters[0]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InitInfo()
|
||||
{
|
||||
var fileVersionInfo = FileVersionInfo.GetVersionInfo(Path.GetFileName(Application.ExecutablePath));
|
||||
_currentVersionInfo = FileVersionInfo.GetVersionInfo(Path.GetFileName(Application.ExecutablePath));
|
||||
|
||||
if (ApplicationDeployment.IsNetworkDeployed)
|
||||
{
|
||||
@@ -196,7 +245,7 @@ namespace ColorControl
|
||||
Text = Application.ProductName + " " + Application.ProductVersion;
|
||||
}
|
||||
|
||||
lblInfo.Text = Text + " - " + fileVersionInfo.LegalCopyright;
|
||||
lblInfo.Text = Text + " - " + _currentVersionInfo.LegalCopyright;
|
||||
|
||||
lbPlugins.Items.Add("lgtv.net by gr4b4z");
|
||||
lbPlugins.Items.Add("Newtonsoft.Json by James Newton-King");
|
||||
@@ -225,6 +274,14 @@ namespace ColorControl
|
||||
WindowState = FormWindowState.Normal;
|
||||
}
|
||||
|
||||
private void trayIconBalloonTip_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_updateHtmlUrl != null)
|
||||
{
|
||||
Utils.StartProcess(_updateHtmlUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void FillNvPresets()
|
||||
{
|
||||
Utils.InitListView(lvNvPresets, NvPreset.GetColumnNames());
|
||||
@@ -507,9 +564,8 @@ namespace ColorControl
|
||||
{
|
||||
var shortcut = edtShortcut.Text.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(shortcut) && !shortcut.Contains("+"))
|
||||
if (!Utils.ValidateShortcut(shortcut))
|
||||
{
|
||||
MessageForms.WarningOk("Invalid shortcut. The shortcut should have modifiers and a normal key.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -541,6 +597,11 @@ namespace ColorControl
|
||||
|
||||
private void MainForm_Resize(object sender, EventArgs e)
|
||||
{
|
||||
if (_skipResize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
if (_config.MinimizeToTray)
|
||||
@@ -570,6 +631,7 @@ namespace ColorControl
|
||||
chkStartMinimized.Checked = _config.StartMinimized;
|
||||
chkMinimizeOnClose.Checked = _config.MinimizeOnClose;
|
||||
chkMinimizeToSystemTray.Checked = _config.MinimizeToTray;
|
||||
chkCheckForUpdates.Checked = _config.CheckForUpdates;
|
||||
edtDelayDisplaySettings.Value = _config.DisplaySettingsDelay;
|
||||
edtBlankScreenSaverShortcut.Text = _config.ScreenSaverShortcut;
|
||||
|
||||
@@ -578,8 +640,16 @@ namespace ColorControl
|
||||
Utils.RegisterShortcut(Handle, SHORTCUTID_SCREENSAVER, _config.ScreenSaverShortcut);
|
||||
}
|
||||
|
||||
Width = _config.FormWidth;
|
||||
Height = _config.FormHeight;
|
||||
_skipResize = true;
|
||||
try
|
||||
{
|
||||
Width = _config.FormWidth;
|
||||
Height = _config.FormHeight;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_skipResize = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
@@ -638,6 +708,9 @@ namespace ColorControl
|
||||
lblLgError.Text = "Error while initializing the LG-controller. You either don't have a LG TV or it is disabled.";
|
||||
lblLgError.Visible = true;
|
||||
}
|
||||
|
||||
InitSelectedTab();
|
||||
CheckForUpdates();
|
||||
}
|
||||
|
||||
protected override void SetVisibleCore(bool value)
|
||||
@@ -1043,6 +1116,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();
|
||||
|
||||
@@ -1067,6 +1145,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
|
||||
{
|
||||
@@ -1075,6 +1163,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1088,9 +1181,8 @@ namespace ColorControl
|
||||
private void btnSetShortcutLg_Click(object sender, EventArgs e)
|
||||
{
|
||||
var shortcut = edtShortcutLg.Text.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(shortcut) && !shortcut.Contains("+"))
|
||||
if (!Utils.ValidateShortcut(shortcut))
|
||||
{
|
||||
MessageForms.WarningOk("Invalid shortcut. The shortcut should have modifiers and a normal key.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1128,6 +1220,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)
|
||||
{
|
||||
@@ -1135,15 +1238,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(',');
|
||||
preset.steps.AddRange(steps);
|
||||
}
|
||||
Utils.ParseWords(preset.steps, text);
|
||||
|
||||
AddOrUpdateItemLg();
|
||||
|
||||
@@ -1198,30 +1294,15 @@ namespace ColorControl
|
||||
}
|
||||
|
||||
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
InitSelectedTab();
|
||||
}
|
||||
|
||||
private void InitSelectedTab()
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -1233,25 +1314,44 @@ namespace ColorControl
|
||||
}
|
||||
else if (tcMain.SelectedTab == tabOptions)
|
||||
{
|
||||
grpNvidiaOptions.Visible = _nvService != null;
|
||||
if (grpNvidiaOptions.Visible)
|
||||
InitOptionsTab();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitLgTab()
|
||||
{
|
||||
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;
|
||||
|
||||
Utils.BuildComboBox<PresetTriggerType>(cbxLgPresetTrigger, PresetTriggerType.Resume, PresetTriggerType.Shutdown, PresetTriggerType.Standby, PresetTriggerType.Startup, PresetTriggerType.Reserved5, PresetTriggerType.ScreensaverStart, PresetTriggerType.ScreensaverStop);
|
||||
}
|
||||
|
||||
private void InitOptionsTab()
|
||||
{
|
||||
grpNvidiaOptions.Visible = _nvService != null;
|
||||
if (grpNvidiaOptions.Visible)
|
||||
{
|
||||
var firstTime = cbxDitheringBitDepth.Items.Count == 0;
|
||||
|
||||
if (firstTime)
|
||||
{
|
||||
var firstTime = cbxDitheringBitDepth.Items.Count == 0;
|
||||
cbxDitheringBitDepth.Items.AddRange(Utils.GetDescriptions<NvDitherBits>().ToArray());
|
||||
cbxDitheringMode.Items.AddRange(Utils.GetDescriptions<NvDitherMode>().ToArray());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1505,7 +1605,6 @@ namespace ColorControl
|
||||
throw new Exception("Error while applying NVIDIA preset. At least one setting could not be applied. Check the log for details.");
|
||||
}
|
||||
|
||||
UpdateDisplayInfoItems();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1529,7 +1628,6 @@ namespace ColorControl
|
||||
throw new Exception("Error while applying AMD preset. At least one setting could not be applied. Check the log for details.");
|
||||
}
|
||||
|
||||
UpdateDisplayInfoItemsAmd();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1571,7 +1669,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();
|
||||
@@ -1645,8 +1743,85 @@ namespace ColorControl
|
||||
private void miLgAddAction_Click(object sender, EventArgs e)
|
||||
{
|
||||
var item = sender as ToolStripItem;
|
||||
var action = item.Tag as LgDevice.InvokableAction;
|
||||
|
||||
AddToLgSteps(item.Text);
|
||||
var text = item.Text;
|
||||
|
||||
var value = string.Empty;
|
||||
|
||||
if (action.EnumType == null)
|
||||
{
|
||||
if (action.MinValue != action.MaxValue)
|
||||
{
|
||||
var values = MessageForms.ShowDialog("Enter value", new[] {
|
||||
new MessageForms.FieldDefinition
|
||||
{
|
||||
Label = "Enter desired " + text,
|
||||
FieldType = MessageForms.FieldType.Numeric,
|
||||
MinValue = action.MinValue,
|
||||
MaxValue = action.MaxValue
|
||||
}
|
||||
});
|
||||
|
||||
if (!values.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
value = values.First().Value.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var dropDownValues = new List<string>();
|
||||
foreach (var enumValue in Enum.GetValues(action.EnumType))
|
||||
{
|
||||
dropDownValues.Add(enumValue.ToString().Replace("_", ""));
|
||||
}
|
||||
|
||||
var values = MessageForms.ShowDialog("Choose value", new[] {
|
||||
new MessageForms.FieldDefinition
|
||||
{
|
||||
Label = "Choose desired " + text,
|
||||
FieldType = MessageForms.FieldType.DropDown,
|
||||
Values = dropDownValues
|
||||
}
|
||||
});
|
||||
|
||||
if (!values.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
value = values.First().Value.ToString();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
text += $"({value})";
|
||||
}
|
||||
|
||||
AddToLgSteps(text);
|
||||
}
|
||||
|
||||
private void miLgAddNvPreset_Click(object sender, EventArgs e)
|
||||
{
|
||||
var item = sender as ToolStripItem;
|
||||
var preset = item.Tag as NvPreset;
|
||||
|
||||
var text = $"NvPreset({preset.name})";
|
||||
|
||||
AddToLgSteps(text);
|
||||
}
|
||||
|
||||
private void miLgAddAmdPreset_Click(object sender, EventArgs e)
|
||||
{
|
||||
var item = sender as ToolStripItem;
|
||||
var preset = item.Tag as AmdPreset;
|
||||
|
||||
var text = $"AmdPreset({preset.name})";
|
||||
|
||||
AddToLgSteps(text);
|
||||
}
|
||||
|
||||
private void AddToLgSteps(string step)
|
||||
@@ -1720,6 +1895,7 @@ See Options to test this functionality."
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
_config.FixChromeFonts = chkFixChromeFonts.Checked;
|
||||
if (chkFixChromeFonts.Checked)
|
||||
{
|
||||
Utils.ExecuteElevated(StartUpParams.ActivateChromeFontFixParam);
|
||||
@@ -1758,9 +1934,8 @@ Do you want to continue?";
|
||||
{
|
||||
var shortcut = edtBlankScreenSaverShortcut.Text.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(shortcut) && !shortcut.Contains("+"))
|
||||
if (!Utils.ValidateShortcut(shortcut))
|
||||
{
|
||||
MessageForms.WarningOk("Invalid shortcut. The shortcut should have modifiers and a normal key.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1865,7 +2040,7 @@ Do you want to continue?";
|
||||
|
||||
var preset = GetSelectedNvPreset();
|
||||
|
||||
preset.ditheringBits = uint.Parse(item.Tag.ToString());
|
||||
preset.ditheringMode = uint.Parse(item.Tag.ToString());
|
||||
|
||||
AddOrUpdateItem();
|
||||
|
||||
@@ -1879,6 +2054,52 @@ Do you want to continue?";
|
||||
{
|
||||
ApplyNvPresetOnStartup();
|
||||
ApplyAmdPresetOnStartup();
|
||||
if (_trayIcon.Visible)
|
||||
{
|
||||
CheckForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForUpdates()
|
||||
{
|
||||
if (!_config.CheckForUpdates || _checkedForUpdates || Debugger.IsAttached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_checkedForUpdates = true;
|
||||
|
||||
var _ = Utils.GetRestJsonAsync("https://api.github.com/repos/maassoft/colorcontrol/releases/latest", InitHandleCheckForUpdates);
|
||||
}
|
||||
|
||||
private void InitHandleCheckForUpdates(dynamic latest)
|
||||
{
|
||||
BeginInvoke(new Action<dynamic>(HandleCheckForUpdates), new[] { latest });
|
||||
}
|
||||
|
||||
private void HandleCheckForUpdates(dynamic latest)
|
||||
{
|
||||
if (latest?.tag_name == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentVersion = Application.ProductVersion;
|
||||
|
||||
var newVersion = latest.tag_name.Value.Substring(1);
|
||||
if (newVersion.CompareTo(currentVersion) > 0)
|
||||
{
|
||||
_updateHtmlUrl = latest.html_url.Value;
|
||||
|
||||
if (_trayIcon.Visible)
|
||||
{
|
||||
_trayIcon.ShowBalloonTip(5000, "Update available", $"Version {newVersion} is available. Click to open the GitHub page", ToolTipIcon.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageForms.InfoOk($"New version {newVersion} is available. Click on the Help-button to open the GitHub page.", "Update available", "https://github.com/Maassoft/ColorControl/releases/tag/v4.0.0.0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyNvPresetOnStartup(int attempts = 5) {
|
||||
@@ -2025,9 +2246,8 @@ Do you want to continue?";
|
||||
{
|
||||
var shortcut = edtAmdShortcut.Text.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(shortcut) && !shortcut.Contains("+"))
|
||||
if (!Utils.ValidateShortcut(shortcut))
|
||||
{
|
||||
MessageForms.WarningOk("Invalid shortcut. The shortcut should have modifiers and a normal key.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2332,7 +2552,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...");
|
||||
|
||||
@@ -2361,9 +2581,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";
|
||||
}
|
||||
@@ -2402,14 +2622,54 @@ Do you want to continue?";
|
||||
var device = _lgService.GetPresetDevice(preset);
|
||||
|
||||
var actions = device?.GetInvokableActions();
|
||||
foreach (var keyValuePair in actions)
|
||||
foreach (var action in actions)
|
||||
{
|
||||
var text = keyValuePair.Key;
|
||||
var text = action.Name;
|
||||
|
||||
var item = mnuLgActions.DropDownItems.Add(text);
|
||||
item.Tag = action;
|
||||
item.Click += miLgAddAction_Click;
|
||||
}
|
||||
|
||||
if (_nvService != null)
|
||||
{
|
||||
mnuLgNvPresets.DropDownItems.Clear();
|
||||
|
||||
foreach (var nvPreset in _nvService.GetPresets())
|
||||
{
|
||||
var text = nvPreset.name;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var item = mnuLgNvPresets.DropDownItems.Add(text);
|
||||
item.Tag = nvPreset;
|
||||
item.Click += miLgAddNvPreset_Click;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_amdService != null)
|
||||
{
|
||||
mnuLgAmdPresets.DropDownItems.Clear();
|
||||
|
||||
foreach (var amdPreset in _amdService.GetPresets())
|
||||
{
|
||||
var text = amdPreset.name;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var item = mnuLgAmdPresets.DropDownItems.Add(text);
|
||||
item.Tag = amdPreset;
|
||||
item.Click += miLgAddAmdPreset_Click;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mnuLgNvPresets.Visible = _nvService != null;
|
||||
mnuLgAmdPresets.Visible = _amdService != null;
|
||||
|
||||
mnuLgNvPresets.Text = mnuLgNvPresets.DropDownItems.Count > 0 ? "NVIDIA presets" : "NVIDIA presets (no named presets found)";
|
||||
mnuLgAmdPresets.Text = mnuLgAmdPresets.DropDownItems.Count > 0 ? "AMD presets" : "AMD presets (no named presets found)";
|
||||
}
|
||||
|
||||
private void btnLgDeviceConvertToCustom_Click(object sender, EventArgs e)
|
||||
@@ -2440,7 +2700,13 @@ Do you want to continue?"
|
||||
|
||||
private void mnuLgExpert_Opening(object sender, CancelEventArgs e)
|
||||
{
|
||||
mnuLgOLEDMotionPro.Visible = _lgService.SelectedDevice?.Name.Contains("C9") ?? _lgService.SelectedDevice?.Name.Contains("B9") ?? false;
|
||||
var device = _lgService.SelectedDevice;
|
||||
|
||||
var eligibleModels = new[] { "B9", "C9", "E9", "W9" };
|
||||
|
||||
var visible = device?.ModelName != null ? eligibleModels.Any(m => device.ModelName.Contains(m)) : false;
|
||||
mnuLgOLEDMotionPro.Visible = visible;
|
||||
miLgExpertSeparator1.Visible = visible;
|
||||
|
||||
if (mnuLgExpertBacklight.DropDownItems.Count == 0)
|
||||
{
|
||||
@@ -2452,6 +2718,18 @@ Do you want to continue?"
|
||||
item.Click += btnLgExpertBacklight_Click;
|
||||
}
|
||||
}
|
||||
|
||||
// Does not work yet, getting a "401 no permissions" error
|
||||
//var task = device?.GetPictureSettings();
|
||||
//var settings = Utils.WaitForTask<dynamic>(task);
|
||||
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Picture Mode", typeof(PictureMode), btnLgExpertColorGamut_Click, "pictureMode");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Color Gamut", typeof(ColorGamut), btnLgExpertColorGamut_Click, "colorGamut");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Dynamic Contrast", typeof(OffToAuto), btnLgExpertColorGamut_Click, "dynamicContrast");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Smooth Gradation", typeof(OffToAuto), btnLgExpertColorGamut_Click, "smoothGradation");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Peak Brightness", typeof(OffToAuto), btnLgExpertColorGamut_Click, "peakBrightness");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "OLED Motion Pro", typeof(OffToHigh), btnLgExpertColorGamut_Click, "motionProOLED");
|
||||
Utils.BuildDropDownMenuEx(mnuLgExpert, "Energy Saving", typeof(EnergySaving), btnLgExpertColorGamut_Click, "energySaving");
|
||||
}
|
||||
|
||||
private void btnLgExpert_Click(object sender, EventArgs e)
|
||||
@@ -2467,6 +2745,15 @@ Do you want to continue?"
|
||||
_lgService.SelectedDevice?.SetBacklight(backlight);
|
||||
}
|
||||
|
||||
private void btnLgExpertColorGamut_Click(object sender, EventArgs e)
|
||||
{
|
||||
var item = sender as ToolStripItem;
|
||||
var name = item.Tag.ToString();
|
||||
var value = item.Text;
|
||||
|
||||
_lgService.SelectedDevice?.SetSystemSettings(name, value);
|
||||
}
|
||||
|
||||
private void miLgEnableMotionPro_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageForms.QuestionYesNo("Are you sure you want to enable OLED Motion Pro? This app and its creator are in no way accountable for any damages it may cause to your tv.") == DialogResult.Yes)
|
||||
@@ -2483,5 +2770,79 @@ Do you want to continue?"
|
||||
|
||||
MessageForms.InfoOk("Setting applied.");
|
||||
}
|
||||
|
||||
private void cbxLgApps_KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
if (e.KeyChar == ((char)Keys.Back))
|
||||
{
|
||||
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(", ");
|
||||
}
|
||||
|
||||
private void chkCheckForUpdates_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
_config.CheckForUpdates = chkCheckForUpdates.Checked;
|
||||
}
|
||||
}
|
||||
|
||||
private void chkLgAllowDowngrade_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (chkLgAllowDowngrade.Checked)
|
||||
{
|
||||
if (MessageForms.QuestionYesNo(
|
||||
@"Are you sure you want to enable firmware downgrade functionality with the Software Update-app?
|
||||
This may cause irreversible damage to your tv and will void your warranty.
|
||||
This app and its creator are in no way accountable for any damages it may cause to your tv."
|
||||
) != DialogResult.Yes)
|
||||
{
|
||||
chkLgAllowDowngrade.Checked = false;
|
||||
return;
|
||||
}
|
||||
MessageForms.InfoOk(
|
||||
@"Firmware downgrade functionality activated.
|
||||
In order to use it, you must create and execute a preset on the LG controller tab that starts the app Software Update (com.webos.app.softwareupdate)."
|
||||
);
|
||||
}
|
||||
|
||||
_lgService.Config.AllowFirmwareDowngrade = chkLgAllowDowngrade.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,9 +129,6 @@
|
||||
<metadata name="mnuLgButtons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>147, 17</value>
|
||||
</metadata>
|
||||
<metadata name="backgroundWorker1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>419, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
|
||||
+158
-24
@@ -8,6 +8,24 @@ namespace ColorControl
|
||||
{
|
||||
class MessageForms
|
||||
{
|
||||
public enum FieldType
|
||||
{
|
||||
Text,
|
||||
Numeric,
|
||||
DropDown,
|
||||
Flags
|
||||
}
|
||||
|
||||
public class FieldDefinition
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public FieldType FieldType { get; set; } = FieldType.Text;
|
||||
public IEnumerable<string> Values { get; set; }
|
||||
public decimal MinValue { get; set; }
|
||||
public decimal MaxValue { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
public static Form MainForm;
|
||||
|
||||
public static void WarningOk(string text)
|
||||
@@ -20,9 +38,16 @@ namespace ColorControl
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
public static void InfoOk(string text)
|
||||
public static void InfoOk(string text, string title = null, string url = null)
|
||||
{
|
||||
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
if (url != null)
|
||||
{
|
||||
MessageBox.Show(text, title ?? MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0, url);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(text, title ?? MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
public static DialogResult QuestionYesNo(string text)
|
||||
@@ -30,63 +55,172 @@ 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 values = new List<string>();
|
||||
var fieldDefinitions = labels.Select(l => new FieldDefinition
|
||||
{
|
||||
Label = l
|
||||
}).ToList();
|
||||
|
||||
return ShowDialog(caption, fieldDefinitions, validateFunc);
|
||||
}
|
||||
|
||||
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<FieldDefinition> fields, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
|
||||
{
|
||||
var values = new List<FieldDefinition>();
|
||||
|
||||
var prompt = new Form()
|
||||
{
|
||||
Width = 500,
|
||||
Height = 90 + (labels.Count() * 55),
|
||||
Height = 90 + (fields.Count() * 55),
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
Text = caption,
|
||||
StartPosition = FormStartPosition.CenterScreen
|
||||
};
|
||||
|
||||
var top = 10;
|
||||
var boxes = new List<TextBoxBase>();
|
||||
var boxes = new List<Control>();
|
||||
|
||||
foreach (var label in labels)
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var textLabel = new Label() { Left = 40, Top = top, Text = label };
|
||||
var label = field.Label;
|
||||
var textLabel = new Label() { Left = 40, Top = top, Text = label, AutoSize = true };
|
||||
top += 25;
|
||||
prompt.Controls.Add(textLabel);
|
||||
|
||||
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
|
||||
Control control;
|
||||
|
||||
if (label.Contains("Ip-address"))
|
||||
switch(field.FieldType)
|
||||
{
|
||||
textBox.Mask = "###.###.###.###";
|
||||
textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
else if (label.Contains("MAC-address"))
|
||||
{
|
||||
textBox.Mask = "AA-AA-AA-AA-AA-AA";
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
case FieldType.Text:
|
||||
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (label.Contains("Ip-address"))
|
||||
{
|
||||
//textBox.Mask = "990.990.990.990";
|
||||
//textBox.ValidatingType = typeof(System.Net.IPAddress);
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
else if (label.Contains("MAC-address"))
|
||||
{
|
||||
textBox.Mask = "AA-AA-AA-AA-AA-AA";
|
||||
textBox.Culture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
control = textBox;
|
||||
break;
|
||||
|
||||
case FieldType.Numeric:
|
||||
var numericEdit = new NumericUpDown() { Left = 40, Top = top, Width = 400 };
|
||||
|
||||
if (field.MinValue != field.MaxValue)
|
||||
{
|
||||
numericEdit.Minimum = field.MinValue;
|
||||
numericEdit.Maximum = field.MaxValue;
|
||||
}
|
||||
|
||||
control = numericEdit;
|
||||
break;
|
||||
|
||||
case FieldType.DropDown:
|
||||
var comboBox = new ComboBox
|
||||
{
|
||||
Left = 40,
|
||||
Top = top,
|
||||
Width = 400,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
|
||||
if (field.Values != null && field.Values.Any())
|
||||
{
|
||||
comboBox.Items.AddRange(field.Values.ToArray());
|
||||
comboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
top += 30;
|
||||
prompt.Controls.Add(textBox);
|
||||
boxes.Add(textBox);
|
||||
prompt.Controls.Add(control);
|
||||
boxes.Add(control);
|
||||
control.Tag = field;
|
||||
}
|
||||
|
||||
var confirmation = new Button() { Text = "Add", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 };
|
||||
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;
|
||||
@@ -51,7 +51,7 @@ namespace ColorControl
|
||||
displayName = preset.displayName;
|
||||
|
||||
var colorData = preset.colorData;
|
||||
this.colorData = new ColorData(colorData.ColorFormat, dynamicRange: colorData.DynamicRange, colorDepth: colorData.ColorDepth, colorSelectionPolicy: colorData.SelectionPolicy);
|
||||
this.colorData = new ColorData(colorData.ColorFormat, dynamicRange: colorData.DynamicRange, colorDepth: colorData.ColorDepth, colorSelectionPolicy: ColorDataSelectionPolicy.User);
|
||||
applyColorData = preset.applyColorData;
|
||||
|
||||
applyHDR = preset.applyHDR;
|
||||
@@ -206,11 +206,16 @@ namespace ColorControl
|
||||
{
|
||||
dynamicRange = (ColorDataDynamicRange)Enum.ToObject(typeof(ColorDataDynamicRange), value);
|
||||
}
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out value))
|
||||
if (dictionary.TryGetValue("SelectionPolicy", out _))
|
||||
{
|
||||
selectionPolicy = (ColorDataSelectionPolicy)Enum.ToObject(typeof(ColorDataSelectionPolicy), value);
|
||||
selectionPolicy =
|
||||
colorDepth == ColorDataDepth.Default &&
|
||||
format >= ColorDataFormat.Default &&
|
||||
colorimetry >= ColorDataColorimetry.Default &&
|
||||
dynamicRange == ColorDataDynamicRange.Auto ?
|
||||
ColorDataSelectionPolicy.BestQuality : selectionPolicy;
|
||||
}
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy);
|
||||
return new ColorData(format, dynamicRange: dynamicRange, colorimetry: colorimetry, colorDepth: colorDepth, colorSelectionPolicy: selectionPolicy, desktopColorDepth: ColorDataDesktopDepth.Default);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-33
@@ -68,9 +68,8 @@ namespace ColorControl
|
||||
};
|
||||
|
||||
private Display _currentDisplay;
|
||||
private NvPreset _lastAppliedPreset;
|
||||
|
||||
public NvService(string dataPath) : base(dataPath)
|
||||
public NvService(string dataPath) : base(dataPath, "NvPresets.json")
|
||||
{
|
||||
var converter = new ColorDataConverter();
|
||||
_JsonDeserializer.RegisterConverters(new[] { converter });
|
||||
@@ -127,20 +126,9 @@ namespace ColorControl
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
protected override List<NvPreset> GetDefaultPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, "NvPresets.json");
|
||||
|
||||
if (File.Exists(_presetsFilename))
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<NvPreset>>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presets = NvPreset.GetDefaultPresets();
|
||||
}
|
||||
return NvPreset.GetDefaultPresets();
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
@@ -254,6 +242,8 @@ namespace ColorControl
|
||||
|
||||
_lastAppliedPreset = preset;
|
||||
|
||||
PresetApplied();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -289,26 +279,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)
|
||||
@@ -488,11 +482,6 @@ namespace ColorControl
|
||||
return list;
|
||||
}
|
||||
|
||||
public NvPreset GetLastAppliedPreset()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
NVIDIA.Initialize();
|
||||
|
||||
+136
-11
@@ -1,36 +1,127 @@
|
||||
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,
|
||||
[Description("Screensaver")]
|
||||
Screensaver = 16,
|
||||
[Description("Process start")]
|
||||
ProcessStart = 32,
|
||||
[Description("Process exit")]
|
||||
ProcessExit = 64
|
||||
Standby = 4,
|
||||
[Description("Reserved5")]
|
||||
Reserved5 = 5,
|
||||
[Description("Process switch")]
|
||||
ProcessSwitch = 6,
|
||||
[Description("Screensaver start")]
|
||||
ScreensaverStart = 7,
|
||||
[Description("Screensaver stop")]
|
||||
ScreensaverStop = 8,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum PresetConditionType
|
||||
{
|
||||
[Description("None")]
|
||||
None = 0,
|
||||
[Description("SDR")]
|
||||
SDR = 1,
|
||||
[Description("HDR")]
|
||||
HDR = 2,
|
||||
[Description("Full Screen")]
|
||||
FullScreen = 4,
|
||||
[Description("Notifications Disabled")]
|
||||
NotificationsDisabled = 8,
|
||||
}
|
||||
|
||||
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 +159,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.1.0.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0.0")]
|
||||
[assembly: AssemblyVersion("4.5.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.5.0.0")]
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"type":"register",
|
||||
"id":"register_0",
|
||||
"payload":{
|
||||
"forcePairing":false,
|
||||
"pairingType":"PROMPT",
|
||||
"client-key":"CLIENTKEYGOESHERE",
|
||||
"manifest":{
|
||||
"manifestVersion":1,
|
||||
"appVersion":"1.1",
|
||||
"signed":{
|
||||
"created":"20140509",
|
||||
"appId":"com.lge.test",
|
||||
"vendorId":"com.lge",
|
||||
"localizedAppNames":{
|
||||
"":"LG Remote App",
|
||||
"ko-KR":"리모컨 앱",
|
||||
"zxx-XX":"ЛГ Rэмotэ AПП"
|
||||
},
|
||||
"localizedVendorNames":{
|
||||
"":"LG Electronics"
|
||||
},
|
||||
"permissions":[
|
||||
"TEST_SECURE",
|
||||
"CONTROL_INPUT_TEXT",
|
||||
"CONTROL_MOUSE_AND_KEYBOARD",
|
||||
"READ_INSTALLED_APPS",
|
||||
"READ_LGE_SDX",
|
||||
"READ_NOTIFICATIONS",
|
||||
"SEARCH",
|
||||
"WRITE_SETTINGS",
|
||||
"WRITE_NOTIFICATION_ALERT",
|
||||
"CONTROL_POWER",
|
||||
"READ_CURRENT_CHANNEL",
|
||||
"READ_RUNNING_APPS",
|
||||
"READ_UPDATE_INFO",
|
||||
"UPDATE_FROM_REMOTE_APP",
|
||||
"READ_LGE_TV_INPUT_EVENTS",
|
||||
"READ_TV_CURRENT_TIME"
|
||||
],
|
||||
"serial":"2f930e2d2cfe083771f68e4fe7bb07"
|
||||
},
|
||||
"permissions":[
|
||||
"LAUNCH",
|
||||
"LAUNCH_WEBAPP",
|
||||
"APP_TO_APP",
|
||||
"CLOSE",
|
||||
"TEST_OPEN",
|
||||
"TEST_PROTECTED",
|
||||
"CONTROL_AUDIO",
|
||||
"CONTROL_DISPLAY",
|
||||
"CONTROL_INPUT_JOYSTICK",
|
||||
"CONTROL_INPUT_MEDIA_RECORDING",
|
||||
"CONTROL_INPUT_MEDIA_PLAYBACK",
|
||||
"CONTROL_INPUT_TV",
|
||||
"CONTROL_POWER",
|
||||
"CONTROL_TV_SCREEN",
|
||||
"READ_APP_STATUS",
|
||||
"READ_CURRENT_CHANNEL",
|
||||
"READ_INPUT_DEVICE_LIST",
|
||||
"READ_NETWORK_STATE",
|
||||
"READ_RUNNING_APPS",
|
||||
"READ_TV_CHANNEL_LIST",
|
||||
"WRITE_NOTIFICATION_TOAST",
|
||||
"READ_POWER_STATE",
|
||||
"READ_COUNTRY_INFO",
|
||||
"READ_SETTINGS"
|
||||
],
|
||||
"signatures":[
|
||||
{
|
||||
"signatureVersion":1,
|
||||
"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
@@ -7,16 +8,25 @@ namespace ColorControl
|
||||
{
|
||||
abstract class ServiceBase<T> where T : PresetBase
|
||||
{
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public event EventHandler<T> AfterApplyPreset;
|
||||
|
||||
protected string _dataPath;
|
||||
protected string _presetsBaseFilename;
|
||||
protected string _presetsFilename;
|
||||
protected string _presetsBackupFilename;
|
||||
protected JavaScriptSerializer _JsonSerializer;
|
||||
protected JavaScriptSerializer _JsonDeserializer;
|
||||
protected bool _initialized = false;
|
||||
protected List<T> _presets;
|
||||
protected T _lastAppliedPreset;
|
||||
protected string _loadPresetsError;
|
||||
|
||||
public ServiceBase(string dataPath)
|
||||
public ServiceBase(string dataPath, string presetsBaseFilename)
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
_presetsBaseFilename = presetsBaseFilename;
|
||||
|
||||
_JsonSerializer = new JavaScriptSerializer();
|
||||
_JsonDeserializer = new JavaScriptSerializer();
|
||||
@@ -45,6 +55,11 @@ namespace ColorControl
|
||||
return preset;
|
||||
}
|
||||
|
||||
public T GetLastAppliedPreset()
|
||||
{
|
||||
return _lastAppliedPreset;
|
||||
}
|
||||
|
||||
protected virtual void Initialize()
|
||||
{
|
||||
|
||||
@@ -54,5 +69,70 @@ namespace ColorControl
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void PresetApplied()
|
||||
{
|
||||
AfterApplyPreset?.Invoke(this, _lastAppliedPreset);
|
||||
}
|
||||
|
||||
protected void LoadPresets()
|
||||
{
|
||||
_presetsFilename = Path.Combine(_dataPath, _presetsBaseFilename);
|
||||
_presetsBackupFilename = Path.Combine(_dataPath, $"{_presetsBaseFilename}.backup");
|
||||
|
||||
try
|
||||
{
|
||||
var presetsExists = File.Exists(_presetsFilename);
|
||||
if (presetsExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_presetsFilename);
|
||||
|
||||
// Hack to convert incorrect triggers
|
||||
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
|
||||
|
||||
_presets = _JsonDeserializer.Deserialize<List<T>>(json);
|
||||
|
||||
if (_presets != null)
|
||||
{
|
||||
if (File.Exists(_presetsBackupFilename))
|
||||
{
|
||||
File.Delete(_presetsBackupFilename);
|
||||
}
|
||||
File.Copy(_presetsFilename, _presetsBackupFilename);
|
||||
}
|
||||
}
|
||||
catch (Exception ex1)
|
||||
{
|
||||
Logger.Error($"Error while loading presets, reverting to default presets: {ex1.Message}");
|
||||
_loadPresetsError = ex1.Message;
|
||||
}
|
||||
}
|
||||
if (!presetsExists || _presets == null)
|
||||
{
|
||||
Logger.Debug("Reverting to default presets");
|
||||
try
|
||||
{
|
||||
_presets = GetDefaultPresets();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Error loading default presets, reverting to empty list: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"General error while loading presets, reverting to empty list: {ex.Message}");
|
||||
_loadPresetsError = ex.Message;
|
||||
}
|
||||
if (_presets == null)
|
||||
{
|
||||
_presets = new List<T>();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<T> GetDefaultPresets();
|
||||
}
|
||||
}
|
||||
+321
-12
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NStandard;
|
||||
using NWin32;
|
||||
using NWin32.NativeTypes;
|
||||
@@ -12,6 +13,7 @@ using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
@@ -35,6 +37,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;
|
||||
@@ -49,7 +68,7 @@ namespace ColorControl
|
||||
|
||||
public static bool ConsoleOpened { get; private set; }
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
@@ -74,6 +93,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);
|
||||
|
||||
@@ -82,6 +107,8 @@ namespace ColorControl
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
private static bool WinKeyDown = false;
|
||||
private static UserNotificationState LastNotificationState;
|
||||
private static Keys[] KeysWithoutModifiers = new[] { Keys.F13, Keys.F14, Keys.F15, Keys.F16, Keys.F17, Keys.F18, Keys.F19, Keys.F20, Keys.F21, Keys.F22, Keys.F23, Keys.F24 };
|
||||
|
||||
public static Bitmap SubPixelShift(Bitmap bitmap)
|
||||
{
|
||||
@@ -427,11 +454,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;
|
||||
@@ -497,7 +528,7 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterShortcut(IntPtr handle, int id, string shortcut, bool clear = false)
|
||||
public static bool RegisterShortcut(IntPtr handle, int id, string shortcut, bool clear = false)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
@@ -507,13 +538,41 @@ namespace ColorControl
|
||||
if (!string.IsNullOrEmpty(shortcut))
|
||||
{
|
||||
var (mods, key) = ParseShortcut(shortcut);
|
||||
RegisterHotKey(handle, id, mods, key);
|
||||
var result = RegisterHotKey(handle, id, mods, key);
|
||||
if (!result)
|
||||
{
|
||||
var errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
||||
Logger.Error($"Could not register shortcut {shortcut}: {errorMessage}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void BuildDropDownMenuEx(ContextMenuStrip mnuParent, string name, Type enumType, EventHandler clickEvent, object tag = null)
|
||||
{
|
||||
var subMenuName = $"{mnuParent.Name}_{name}";
|
||||
var subMenuItems = mnuParent.Items.Find(subMenuName, false);
|
||||
ToolStripMenuItem subMenuItem;
|
||||
if (subMenuItems.Length == 0)
|
||||
{
|
||||
subMenuItem = (ToolStripMenuItem)mnuParent.Items.Add(name);
|
||||
subMenuItem.Name = subMenuName;
|
||||
|
||||
foreach (var enumValue in Enum.GetValues(enumType))
|
||||
{
|
||||
var item = subMenuItem.DropDownItems.Add(enumValue.ToString());
|
||||
item.Tag = tag;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BuildDropDownMenu(ToolStripMenuItem mnuParent, string name, Type enumType, object colorData, string propertyName, EventHandler clickEvent)
|
||||
public static void BuildDropDownMenu(ToolStripDropDownItem mnuParent, string name, Type enumType, object colorData, string propertyName, EventHandler clickEvent)
|
||||
{
|
||||
PropertyInfo property;
|
||||
PropertyInfo property = null;
|
||||
var subMenuItems = mnuParent.DropDownItems.Find("miColorSettings_" + name, false);
|
||||
ToolStripMenuItem subMenuItem;
|
||||
if (subMenuItems.Length == 0)
|
||||
@@ -521,8 +580,11 @@ namespace ColorControl
|
||||
subMenuItem = (ToolStripMenuItem)mnuParent.DropDownItems.Add(name);
|
||||
subMenuItem.Name = "miColorSettings_" + name;
|
||||
|
||||
property = colorData.GetType().GetDeclaredProperty(propertyName);
|
||||
subMenuItem.Tag = property;
|
||||
if (colorData != null)
|
||||
{
|
||||
property = colorData.GetType().GetDeclaredProperty(propertyName);
|
||||
subMenuItem.Tag = property;
|
||||
}
|
||||
|
||||
foreach (var enumValue in Enum.GetValues(enumType))
|
||||
{
|
||||
@@ -537,6 +599,10 @@ namespace ColorControl
|
||||
property = (PropertyInfo)subMenuItem.Tag;
|
||||
}
|
||||
|
||||
if (colorData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var value = property.GetValue(colorData);
|
||||
|
||||
foreach (var item in subMenuItem.DropDownItems)
|
||||
@@ -552,6 +618,63 @@ 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)
|
||||
{
|
||||
if (comboBox.Items.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < comboBox.Items.Count; i++)
|
||||
{
|
||||
var item = comboBox.Items[i] as EnumComboBoxItem;
|
||||
if (item.Id == value)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.SelectedIndex = index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public static T GetComboBoxEnumItem<T>(ComboBox comboBox) where T : IConvertible
|
||||
{
|
||||
if (comboBox.SelectedIndex == -1)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), "None");
|
||||
}
|
||||
|
||||
var item = comboBox.SelectedItem as EnumComboBoxItem;
|
||||
|
||||
return (T)Enum.Parse(typeof(T), item.Id.ToString());
|
||||
}
|
||||
|
||||
public static string FormatKeyboardShortcut(KeyEventArgs keyEvent)
|
||||
{
|
||||
var pressedModifiers = keyEvent.Modifiers;
|
||||
@@ -569,12 +692,17 @@ namespace ColorControl
|
||||
shortcutString += "Win";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(shortcutString) && keyEvent.KeyCode != Keys.ControlKey && keyEvent.KeyCode != Keys.ShiftKey && keyEvent.KeyCode != Keys.Menu && keyEvent.KeyCode != Keys.LWin)
|
||||
var empty = string.IsNullOrEmpty(shortcutString);
|
||||
if ((!empty || KeysWithoutModifiers.Contains(keyEvent.KeyCode)) && keyEvent.KeyCode != Keys.ControlKey && keyEvent.KeyCode != Keys.ShiftKey && keyEvent.KeyCode != Keys.Menu && keyEvent.KeyCode != Keys.LWin)
|
||||
{
|
||||
shortcutString += " + " + keyEvent.KeyCode.ToString();
|
||||
if (!empty)
|
||||
{
|
||||
shortcutString += " + ";
|
||||
}
|
||||
shortcutString += keyEvent.KeyCode.ToString();
|
||||
}
|
||||
|
||||
if (pressedModifiers == 0 && !WinKeyDown)
|
||||
if (string.IsNullOrEmpty(shortcutString))
|
||||
{
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
}
|
||||
@@ -582,6 +710,23 @@ namespace ColorControl
|
||||
return shortcutString;
|
||||
}
|
||||
|
||||
public static bool ValidateShortcut(string shortcut)
|
||||
{
|
||||
var valid = string.IsNullOrWhiteSpace(shortcut) || shortcut.Contains("+") || ShortcutIsAllowedWithoutModifier(shortcut);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
MessageForms.WarningOk("Invalid shortcut. The shortcut should have modifiers and a normal key or be F13-F24.");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public static bool ShortcutIsAllowedWithoutModifier(string shortcut)
|
||||
{
|
||||
return Enum.TryParse<Keys>(shortcut, out var key) && KeysWithoutModifiers.Contains(key);
|
||||
}
|
||||
|
||||
public static void HandleKeyboardShortcutUp(KeyEventArgs keyEvent)
|
||||
{
|
||||
if (keyEvent.KeyCode == Keys.LWin)
|
||||
@@ -647,7 +792,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -715,6 +867,163 @@ namespace ColorControl
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsForegroundFullScreenAndDisabledNotifications(Screen screen = null)
|
||||
{
|
||||
return IsNotificationDisabled() && IsForegroundFullScreen(screen);
|
||||
}
|
||||
|
||||
public static bool IsNotificationDisabled()
|
||||
{
|
||||
var result = SHQueryUserNotificationState(out var state);
|
||||
|
||||
if (result == NativeConstants.ERROR_SUCCESS && state != LastNotificationState)
|
||||
{
|
||||
Logger.Debug($"Detected NotificationState change from {LastNotificationState} to {state}");
|
||||
LastNotificationState = state;
|
||||
}
|
||||
|
||||
return NotificationsDisabledStates.Contains(state);
|
||||
//return state != UserNotificationState.QUNS_ACCEPTS_NOTIFICATIONS && state != UserNotificationState.QUNS_QUIET_TIME;
|
||||
}
|
||||
|
||||
public static bool IsForegroundFullScreen(Screen screen = null)
|
||||
{
|
||||
if (screen == null)
|
||||
{
|
||||
screen = Screen.PrimaryScreen;
|
||||
}
|
||||
var hWnd = NativeMethods.GetForegroundWindow();
|
||||
|
||||
NativeMethods.GetWindowRect(hWnd, out var rect);
|
||||
|
||||
/* in case you want the process name:
|
||||
uint procId = 0;
|
||||
GetWindowThreadProcessId(hWnd, out procId);
|
||||
var proc = System.Diagnostics.Process.GetProcessById((int)procId);
|
||||
Console.WriteLine(proc.ProcessName);
|
||||
*/
|
||||
|
||||
return screen.Bounds.Width <= (rect.right - rect.left) && screen.Bounds.Height <= (rect.bottom - rect.top);
|
||||
}
|
||||
|
||||
public static (uint, bool) GetForegroundProcessIdAndIfFullScreen(Screen screen = null)
|
||||
{
|
||||
if (screen == null)
|
||||
{
|
||||
screen = Screen.PrimaryScreen;
|
||||
}
|
||||
var hWnd = NativeMethods.GetForegroundWindow();
|
||||
|
||||
if (hWnd == IntPtr.Zero)
|
||||
{
|
||||
return (0, false);
|
||||
}
|
||||
|
||||
NativeMethods.GetWindowRect(hWnd, out var rect);
|
||||
|
||||
GetWindowThreadProcessId(hWnd, out var processId);
|
||||
|
||||
var isFullScreen = screen.Bounds.Width <= (rect.right - rect.left) && screen.Bounds.Height <= (rect.bottom - rect.top);
|
||||
|
||||
return (processId, isFullScreen);
|
||||
}
|
||||
|
||||
public static Process GetForegroundFullScreenProcess(Screen screen = null)
|
||||
{
|
||||
var (processId, isFullScreen) = GetForegroundProcessIdAndIfFullScreen(screen);
|
||||
|
||||
if (!isFullScreen)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Process.GetProcessById((int)processId);
|
||||
}
|
||||
|
||||
public static void 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 static int ParseInt(string value, int def = 0)
|
||||
{
|
||||
if (int.TryParse(value, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
public static int ParseDynamicAsInt(dynamic value, int def = 0)
|
||||
{
|
||||
if (value.Type == JTokenType.Integer)
|
||||
{
|
||||
return (int)value.Value;
|
||||
}
|
||||
if (value.Type == JTokenType.String)
|
||||
{
|
||||
return ParseInt(value.Value, def);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public static async Task<dynamic> GetRestJsonAsync(string url, Action<dynamic> callBack = null)
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
|
||||
|
||||
var response = await httpClient.GetAsync(url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var result = await response.Content.ReadAsAsync<dynamic>();
|
||||
|
||||
if (callBack != null)
|
||||
{
|
||||
callBack(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (callBack != null)
|
||||
{
|
||||
callBack(null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumComboBoxItem
|
||||
{
|
||||
public string Name;
|
||||
public int Id;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProcessExtensions
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+211
-19
@@ -16,6 +16,92 @@ namespace LgTv
|
||||
OK,
|
||||
Exit
|
||||
}
|
||||
|
||||
public enum PictureMode
|
||||
{
|
||||
cinema, eco, expert1, expert2, game,
|
||||
normal, photo, sports, technicolor, vivid, hdrEffect, hdrCinema,
|
||||
hdrCinemaBright, hdrExternal, hdrGame, hdrStandard, hdrTechnicolor,
|
||||
hdrVivid, dolbyHdrCinema, dolbyHdrCinemaBright, dolbyHdrDarkAmazon,
|
||||
dolbyHdrGame, dolbyHdrStandard, dolbyHdrVivid, dolbyStandard
|
||||
}
|
||||
|
||||
public enum OffToHigh
|
||||
{
|
||||
off,
|
||||
low,
|
||||
medium,
|
||||
high
|
||||
}
|
||||
|
||||
public enum LowToAuto
|
||||
{
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum OffToOn
|
||||
{
|
||||
off,
|
||||
on
|
||||
}
|
||||
|
||||
public enum OffToAuto2
|
||||
{
|
||||
off,
|
||||
on,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum OffToAuto
|
||||
{
|
||||
off,
|
||||
on,
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
auto
|
||||
}
|
||||
|
||||
public enum ColorGamut
|
||||
{
|
||||
auto,
|
||||
extended,
|
||||
wide,
|
||||
srgb
|
||||
}
|
||||
|
||||
public enum EnergySaving
|
||||
{
|
||||
auto,
|
||||
off,
|
||||
min,
|
||||
med,
|
||||
max,
|
||||
screen_off
|
||||
}
|
||||
|
||||
public enum DynamicTonemapping
|
||||
{
|
||||
on,
|
||||
off,
|
||||
HGIG,
|
||||
}
|
||||
|
||||
public enum TruMotionMode
|
||||
{
|
||||
off,
|
||||
user
|
||||
}
|
||||
|
||||
public enum FalseToTrue
|
||||
{
|
||||
false_,
|
||||
true_
|
||||
}
|
||||
|
||||
public class LgTvApi:IDisposable
|
||||
{
|
||||
public bool ConnectionClosed { get => _connection?.ConnectionClosed ?? true; }
|
||||
@@ -28,17 +114,6 @@ namespace LgTv
|
||||
private List<Channel> _channelList;
|
||||
private List<App> _appList;
|
||||
private string _ip;
|
||||
|
||||
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
|
||||
|
||||
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
|
||||
|
||||
public Task ControlButton(object oK)
|
||||
{
|
||||
// throw new NotImplementedException();
|
||||
return null;
|
||||
}
|
||||
|
||||
private string _currentPairKey;
|
||||
private string webSocketUri;
|
||||
|
||||
@@ -75,17 +150,21 @@ namespace LgTv
|
||||
|
||||
public async Task MakeHandShake()
|
||||
{
|
||||
var registerJson = Utils.GetResourceFile("LG_register.json");
|
||||
|
||||
_currentPairKey = _keyStore.GetClientKey();
|
||||
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0'))
|
||||
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(registerJson))
|
||||
{
|
||||
var key = AFTER_PAIR_HAND_SHAKE.Replace("CLIENTKEYGOESHERE", _currentPairKey);
|
||||
var key = registerJson.Replace("CLIENTKEYGOESHERE", _currentPairKey);
|
||||
var conn = await _connection.SendCommandAsync(key);
|
||||
_keyStore.SaveClientKey((string)conn.clientKey);
|
||||
return;
|
||||
}
|
||||
|
||||
dynamic result = await _connection.SendCommandAsync(BEFORE_PAIR_HAND_SHAKE);
|
||||
var registerJsonRemovedKey = registerJson.Replace("CLIENTKEYGOESHERE", string.Empty);
|
||||
dynamic result = await _connection.SendCommandAsync(registerJsonRemovedKey);
|
||||
_keyStore.SaveClientKey(result.clientKey);
|
||||
_keyStore.SaveHandShake(registerJson);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +306,17 @@ namespace LgTv
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("3d", "ssap://com.webos.service.tv.display/set3DOff"));
|
||||
}
|
||||
|
||||
public async Task TurnScreenOff()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOffScreen"));
|
||||
}
|
||||
|
||||
public async Task TurnScreenOn()
|
||||
{
|
||||
await _connection.SendCommandAsync(new RequestMessage("", "ssap://com.webos.service.tvpower/power/turnOnScreen"));
|
||||
}
|
||||
|
||||
public async Task<bool> IsTurnedOn3D()
|
||||
{
|
||||
//Response: { returnValue: true, status3D: { status: true, pattern: ’2Dto3D’ } }
|
||||
@@ -256,7 +346,7 @@ namespace LgTv
|
||||
}
|
||||
public async Task<IEnumerable<ExternalInput>> GetInputList()
|
||||
{
|
||||
var requestMessage = new RequestMessage("input","ssap://tv/getExternalInputList");
|
||||
var requestMessage = new RequestMessage("input","ssap://tv/getExternalInputList");
|
||||
var results = await _connection.SendCommandAsync(requestMessage);
|
||||
var l = new List<ExternalInput>();
|
||||
foreach (var result in results)
|
||||
@@ -275,6 +365,36 @@ namespace LgTv
|
||||
await _connection.SendCommandAsync(requestMessage);
|
||||
}
|
||||
|
||||
public async Task SubscribeVolume(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://audio/getVolume", null, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task SubscribePowerState(Func<dynamic, bool> callback, dynamic payload = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://com.webos.service.tvpower/power/getPowerState", null, "subscribe");
|
||||
|
||||
await _connection.SubscribeAsync(requestMessage, callback);
|
||||
}
|
||||
|
||||
public async Task 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>();
|
||||
@@ -333,9 +453,19 @@ namespace LgTv
|
||||
return appList.OrderBy(e => e.title);
|
||||
}
|
||||
|
||||
public async Task<string> LaunchApp(string appId)
|
||||
public async Task<string> LaunchApp(string appId, dynamic @params = null)
|
||||
{
|
||||
var requestMessage = new RequestMessage("ssap://system.launcher/launch", new { id = appId });
|
||||
dynamic payload;
|
||||
if (@params != null)
|
||||
{
|
||||
payload = new { id = appId, @params = @params };
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = new { id = appId };
|
||||
}
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://system.launcher/launch", payload);
|
||||
var response = await _connection.SendCommandAsync(requestMessage);
|
||||
return (string)response.sessionId;
|
||||
}
|
||||
@@ -365,11 +495,37 @@ namespace LgTv
|
||||
return (string)response.sessionId;
|
||||
}
|
||||
|
||||
public async Task SetSystemSettings(string key, string value)
|
||||
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
|
||||
{
|
||||
if (key.Contains("_"))
|
||||
{
|
||||
var keys = key.Split('_');
|
||||
key = keys[0];
|
||||
var childKey = keys[1];
|
||||
|
||||
jsonValue = @"{ """ + childKey + @""": " + value + " }";
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonValue = $"\"{value}\"";
|
||||
}
|
||||
}
|
||||
|
||||
var lunauri = "luna://com.webos.settingsservice/setSystemSettings";
|
||||
|
||||
var @params = JObject.Parse(@"{ ""category"": ""picture"", ""settings"": { """ + key + @""": """ + value + @""" } }");
|
||||
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" } }";
|
||||
|
||||
var @params = JObject.Parse(json);
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
@@ -383,6 +539,15 @@ namespace LgTv
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task SetConfig(string key, bool value)
|
||||
{
|
||||
var lunauri = "luna://com.webos.service.config/setConfigs";
|
||||
|
||||
var @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": " + value.ToString().ToLowerInvariant() + @" } }");
|
||||
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
private async Task ExecuteRequest(string lunauri, object @params)
|
||||
{
|
||||
var buttons = new[]
|
||||
@@ -417,6 +582,33 @@ namespace LgTv
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemInfo(params string[] keys)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
keys = keys
|
||||
};
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://system/getSystemInfo", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemSettings(string category, params string[] keys)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
category = category,
|
||||
keys = keys
|
||||
};
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_connection.Close();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -29,6 +29,7 @@ namespace LgTv
|
||||
|
||||
public event IsConnectedDelegate IsConnected;
|
||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<dynamic>> _tokens = new ConcurrentDictionary<string, TaskCompletionSource<dynamic>>();
|
||||
private readonly ConcurrentDictionary<string, Func<dynamic, bool>> _callbacks = new ConcurrentDictionary<string, Func<dynamic, bool>>();
|
||||
|
||||
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
@@ -44,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)
|
||||
{
|
||||
@@ -84,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)
|
||||
{
|
||||
@@ -99,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)
|
||||
{
|
||||
@@ -124,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()
|
||||
@@ -132,7 +144,21 @@ namespace LgTv
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
});
|
||||
return SendCommandAsync(rawMessage.Id, serialized);
|
||||
return await SendCommandAsync(rawMessage.Id, serialized);
|
||||
}
|
||||
|
||||
public async Task<dynamic> SubscribeAsync(RequestMessage message, Func<dynamic, bool> callback)
|
||||
{
|
||||
var rawMessage = new RawRequestMessage(message, ++_commandCount);
|
||||
var serialized = JsonConvert.SerializeObject(rawMessage, new JsonSerializerSettings()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
});
|
||||
|
||||
_callbacks.TryAdd(rawMessage.Id, callback);
|
||||
|
||||
return await SendCommandAsync(rawMessage.Id, serialized);
|
||||
}
|
||||
|
||||
private void Connection_Closed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
@@ -146,7 +172,17 @@ namespace LgTv
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var dr = args.GetDataReader())
|
||||
var task = new Task<DataReader>(new Func<DataReader>(args.GetDataReader));
|
||||
task.Start();
|
||||
var result = task.Wait(5000);
|
||||
|
||||
var dr = result ? task.Result : null;
|
||||
if (!result)
|
||||
{
|
||||
throw new Exception("Timeout while reading response, possible disconnect");
|
||||
}
|
||||
|
||||
using (dr)
|
||||
{
|
||||
dr.UnicodeEncoding = UnicodeEncoding.Utf8;
|
||||
var message = dr.ReadString(dr.UnconsumedBufferLength);
|
||||
@@ -164,30 +200,68 @@ namespace LgTv
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (_tokens.TryGetValue(id, out taskCompletion))
|
||||
else if (_tokens.TryGetValue(id, out taskCompletion))
|
||||
{
|
||||
if (id == "register_0") return;
|
||||
if (obj.type == "error")
|
||||
{
|
||||
taskCompletion.SetException(new Exception(obj.error));
|
||||
taskCompletion.SetException(new Exception(obj.error?.ToString()));
|
||||
}
|
||||
//else if (args.Cancelled)
|
||||
//{
|
||||
// taskSource.SetCanceled();
|
||||
//}
|
||||
taskCompletion.TrySetResult(obj.payload);
|
||||
|
||||
if (_callbacks.TryGetValue(id, out Func<dynamic, bool> callback))
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(obj.payload);
|
||||
}
|
||||
catch (Exception callbackException)
|
||||
{
|
||||
Logger.Error($"Connection_MessageReceived: the callback threw an exception: {callbackException.ToLogString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.Message}");
|
||||
Logger.Error($"Connection_MessageReceived: status: {status}, exception: {ex.ToLogString()}");
|
||||
|
||||
SetExceptionOnAllTokens(ex);
|
||||
|
||||
ConnectionClosed = true;
|
||||
_messageWriter?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetExceptionOnAllTokens(Exception ex)
|
||||
{
|
||||
//Logger.Debug($"Tokens: {_tokens.Count}");
|
||||
|
||||
foreach (var taskCompletion in _tokens.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (taskCompletion.Task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
Logger.Debug("taskCompletion.Task.Status: " + taskCompletion.Task.Status);
|
||||
taskCompletion.SetException(new Exception(ex.Message));
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
Logger.Error("SetExceptionOnAllTokens: " + ex2.Message);
|
||||
}
|
||||
}
|
||||
|
||||
_tokens.Clear();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_connection?.Close(1, "");
|
||||
|
||||
@@ -9,10 +9,14 @@ namespace LgTv
|
||||
Prefix = prefix;
|
||||
Uri = uri;
|
||||
}
|
||||
public RequestMessage(string uri,object payload)
|
||||
public RequestMessage(string uri, object payload, string type = "request")
|
||||
{
|
||||
Uri = uri;
|
||||
Payload = JsonConvert.SerializeObject(payload);
|
||||
if (payload != null)
|
||||
{
|
||||
Payload = JsonConvert.SerializeObject(payload);
|
||||
}
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Native" version="0.1.0" targetFramework="net472" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.7" targetFramework="net472" />
|
||||
<package id="Native" version="0.1.1" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
<package id="NLog" version="4.7.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" />
|
||||
|
||||
@@ -1,19 +1,55 @@
|
||||
# ColorControl
|
||||
Easily change NVIDIA display settings and/or control LG TV's
|
||||
Easily change NVIDIA and AMD display settings and/or control LG TV's
|
||||
|
||||
If you own a NVIDIA graphics card, this app allows you not to only adjust basic display settings, but some hidden settings as well:
|
||||
## NVIDIA/AMD controller
|
||||
|
||||

|
||||
If you own a NVIDIA or AMD graphics card, this app allows you not to only adjust basic display settings, but some hidden settings as well.
|
||||
For both NVIDIA and AMD graphics cards, you can configure your own presets to change the color depth (6 to 16 bpc), color format (RGB/YUV), refresh rate, dithering and HDR setting. You can assign a global keyboard shortcut to each preset to change the display settings (and HDR!) with just a couple of key presses.
|
||||
The NVIDIA controller even has some more options:
|
||||
* Dynamic range: VESA (Full RGB) or CEA (Limited RGB/YUV)
|
||||
* Color space: to change the color space, but most may not be supported by your tv
|
||||
* Dithering: you can define the dithering mode (Temporal or Spatial) and the dithering bit depth
|
||||
|
||||
And if you own a recent LG TV, you can control your TV through the app (no NVIDIA graphics card needed):
|
||||
Notes:
|
||||
* For a specific setting to be applied, you must include it within the preset. Just click the "Include" menu item and once it is checked, you're good to go.
|
||||
|
||||

|
||||
Screenshot:
|
||||

|
||||
|
||||
For this to work correctly:
|
||||
## LG controller
|
||||
|
||||
If you own a recent LG TV (2018 or newer, older might work), you can control your TV through the app (no NVIDIA or AMD graphics card needed).
|
||||
At startup of the application it will automatically detect your tv's (see below) if they are on the same network as your pc. If a tv is powered on, it will show a popup by which you can allow ColorControl to send commands to you tv. This will only happen the first time or whenever there's a change in the required permissions. A new version of ColorControl might need this.
|
||||
It is also possible to add a tv manually by using the "Add" button. A name and ip address are required, the MAC address is only necessary for Wake-On-Lan.
|
||||
You can configure as well when to automatically power your tv on or off:
|
||||
* Power on after startup of pc
|
||||
* Power on after resume
|
||||
* Power off on shutdown
|
||||
* Power off on standby
|
||||
* Power off on screensaver and on when screensaver deactivates
|
||||
|
||||
### Presets
|
||||
|
||||
With the presets you can peform actions on your tv you would normally do via the remote control. Properties of a preset:
|
||||
* Name: fill in your own name/description
|
||||
* Device: select the tv to perform the action on. Defaults to "Globally selected device", which is the selected device in the top devices drop down.
|
||||
* App: select the app to launch that is installed on your tv (optional)
|
||||
* Shortcut: enter the global shortcut to execute this preset
|
||||
* Steps: steps to execute sequentially. These steps can be:
|
||||
* Remote control buttons: like RIGHT, LEFT, ENTER, etc.
|
||||
* Actions: directly change picture settings like backlight, contrast, pictureMode, etc. In a dialog you have to specify the value.
|
||||
* NVIDIA/AMD presets: add NVIDIA or AMD presets here that have to execute as well
|
||||
|
||||
Furthermore, you can add a trigger to a preset which means it will execute automatically when a process on your pc is running. See for more information: https://github.com/Maassoft/ColorControl/releases/tag/v4.0.0.0
|
||||
|
||||
Screenshot:
|
||||

|
||||
|
||||
### Auto detecting your tv
|
||||
Check if the TV is listed in Windows Device Manager (Win+X -> Device Manager) under Digital Media Devices. If not then add the TV using Settings (Win+I) -> "Devices" -> "Add Bluetooth or other device" -> "Everything Else", then select your TV by name. It should now appear in Device Manager. (If your TV is not shown when adding devices then your PC is unable to see the TV on the network, check your network settings on both the PC & TV)
|
||||
NOTE: You may have to add the TV as a device more than once before it appears in Device Manager, as Windows can detect the TV as multiple devices.
|
||||
|
||||
If you receive WinPcap errors download and install Npcap (https://nmap.org/npcap/#download) in WinPcap compatibility mode. WinPcap is depreciated under windows 10.
|
||||
WinPcap is no longer used by default, but if you receive WinPcap errors, download and install Npcap (https://nmap.org/npcap/#download) in WinPcap compatibility mode. WinPcap is depreciated under windows 10.
|
||||
|
||||
On the Options-tabpage you can finetune some parameters and/or enable some settings:
|
||||
|
||||
|
||||
Reference in new issue
Block a user