Compare commits

...
27 Commits
Author SHA1 Message Date
Vincent Maas 815710c081 fix shortcuts and refresh of apps 2021-08-30 19:17:34 +02:00
Vincent Maas 805c516371 fix F13-F24 keys 2021-08-29 16:14:17 +02:00
Vincent Maas b85d929890 fixed process monitor issue, improve shortcuts and Chrome fix 2021-08-29 15:28:18 +02:00
Vincent Maas dd9ce932fc made loading presets code more robust 2021-08-18 11:15:56 +02:00
Vincent Maas 3667243fc7 fixed some issues, added check for updates 2021-08-16 13:59:01 +02:00
Vincent Maas 92f4ab8ef5 add triggers, fixed some bugs 2021-08-13 15:11:33 +02:00
Vincent Maas 90ca095954 add nvidia/amd presets to steps in LG controller 2021-07-04 16:18:59 +02:00
Vincent Maas fa63d8dc4f fixed some issues 2021-06-20 13:59:12 +02:00
Vincent Maas fc134b146a extend eligible models for motion pro, fix bug when adding WOL action 2021-06-11 20:07:51 +02:00
Vincent Maas 3abf106496 extended expert-button, added new actions 2021-06-06 15:58:21 +02:00
Vincent Maas a17534abcb use modelName for detection if eligible for motion pro 2021-05-30 17:17:46 +02:00
Vincent Maas 4614886954 add LG expert button to test new way of changing settings 2021-05-30 10:31:35 +02:00
Vincent Maas 26a91dc7e9 manually add device, add device setting per preset, add remote control image 2021-05-02 17:38:38 +02:00
Vincent Maas cac5180c76 add name to NVIDIA/AMD presets, add CLI 2021-04-18 15:15:55 +02:00
Vincent Maas 0c877623c0 add screensaver trigger, focus main window when already started, fix some bugs 2021-04-03 16:27:59 +02:00
Vincent Maas a33543acd5 add support for AMD GPU's, improve WOL, refactor code 2021-03-20 14:35:58 +01:00
Vincent Maas 7f6175f8ef add WOL to steps, fix saving configs 2021-02-14 15:17:30 +01:00
Vincent Maas fd51bcca99 add alternate WOL function 2021-02-07 10:45:53 +01:00
Vincent Maas 5268e6679e add better logging of network devices when sending WOL 2021-02-05 19:56:19 +01:00
Vincent Maas 91fd44d698 try to wake device first when lookup fails 2021-02-02 19:43:14 +01:00
Vincent Maas 48b13b1c64 improve checking on attached displays 2021-02-01 19:26:03 +01:00
Vincent Maas d2aacbb8e0 fix crash on startup when no displays are active, set thread execution state when powering off tv on standby 2021-01-30 15:23:06 +01:00
Vincent Maas 04d52d6062 add apply on startup, improve remote controller, fixed some bugs 2021-01-24 15:35:00 +01:00
Vincent Maas 13420d2855 Merge branch 'master' of https://github.com/Maassoft/ColorControl 2021-01-11 19:44:04 +01:00
Vincent Maas 43a4660cc5 add some buttons to control LG TV (volume/mute)
add preliminary remote control form
2021-01-11 19:43:53 +01:00
Maassoft 8683cd5be0 Update README.md 2021-01-01 14:32:02 +01:00
Vincent Maas 038a70cc81 add testing/customization of dithering 2020-12-29 19:24:25 +01:00
73 changed files with 11256 additions and 1972 deletions

No files matched your search

+90 -228
View File
@@ -23,12 +23,10 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using FARPROC = System.IntPtr;
using HMODULE = System.IntPtr;
using ADL_CONTEXT_HANDLE = System.IntPtr;
#endregion Using
@@ -36,52 +34,6 @@ using HMODULE = System.IntPtr;
namespace ATI.ADL
{
#region Export Delegates
/// <summary> ADL Memory allocation function allows ADL to callback for memory allocation</summary>
/// <param name="size">input size</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate IntPtr ADL_Main_Memory_Alloc (int size);
// ///// <summary> ADL Create Function to create ADL Data</summary>
/// <param name="callback">Call back functin pointer which is ised to allocate memeory </param>
/// <param name="enumConnectedAdapters">If it is 1, then ADL will only retuen the physical exist adapters </param>
///// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters);
/// <summary> ADL Destroy Function to free up ADL Data</summary>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Destroy ();
/// <summary> ADL Function to get the number of adapters</summary>
/// <param name="numAdapters">return number of adapters</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Adapter_NumberOfAdapters_Get (ref int numAdapters);
/// <summary> ADL Function to get the GPU adapter information</summary>
/// <param name="info">return GPU adapter information</param>
/// <param name="inputSize">the size of the GPU adapter struct</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Adapter_AdapterInfo_Get (IntPtr info, int inputSize);
/// <summary> Function to determine if the adapter is active or not.</summary>
/// <remarks>The function is used to check if the adapter associated with iAdapterIndex is active</remarks>
/// <param name="adapterIndex"> Adapter Index.</param>
/// <param name="status"> Status of the adapter. True: Active; False: Dsiabled</param>
/// <returns>Non zero is successfull</returns>
internal delegate int ADL_Adapter_Active_Get(int adapterIndex, ref int status);
/// <summary>Get display information based on adapter index</summary>
/// <param name="adapterIndex">Adapter Index</param>
/// <param name="numDisplays">return the total number of supported displays</param>
/// <param name="displayInfoArray">return ADLDisplayInfo Array for supported displays' information</param>
/// <param name="forceDetect">force detect or not</param>
/// <returns>return ADL Error Code</returns>
internal delegate int ADL_Display_DisplayInfo_Get(int adapterIndex, ref int numDisplays, out IntPtr displayInfoArray, int forceDetect);
internal delegate int ADL_Display_Size_Get(int adapterIndex, int displayIndex, ref int lpWidth, ref int lpHeight, ref int lpDefaultWidth, ref int lpDefaultHeight, ref int lpMinWidth, ref int lpMinHeight, ref int lpMaxWidth, ref int lpMaxHeight, ref int lpStepWidth, ref int lpStepHeight);
#endregion Export Delegates
#region Export Struct
#region ADLAdapterInfo
@@ -207,10 +159,74 @@ namespace ATI.ADL
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8;
/// <summary> Maximum number of ADLMOdes for the adapter </summary>
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
internal static Dictionary<Type, object> ADLDelegates = new Dictionary<Type, object>();
#endregion Internal Constant
#region Export Delegates
/// <summary> ADL Memory allocation function allows ADL to callback for memory allocation</summary>
/// <param name="size">input size</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate IntPtr ADL_Main_Memory_Alloc(int size);
// ///// <summary> ADL Create Function to create ADL Data</summary>
/// <param name="callback">Call back functin pointer which is ised to allocate memeory </param>
/// <param name="enumConnectedAdapters">If it is 1, then ADL will only retuen the physical exist adapters </param>
///// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters);
internal delegate int ADL2_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters, ref ADL_CONTEXT_HANDLE context);
/// <summary> ADL Destroy Function to free up ADL Data</summary>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Main_Control_Destroy();
internal delegate int ADL2_Main_Control_Destroy(ADL_CONTEXT_HANDLE context);
/// <summary> ADL Function to get the number of adapters</summary>
/// <param name="numAdapters">return number of adapters</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Adapter_NumberOfAdapters_Get(ref int numAdapters);
/// <summary> ADL Function to get the GPU adapter information</summary>
/// <param name="info">return GPU adapter information</param>
/// <param name="inputSize">the size of the GPU adapter struct</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate int ADL_Adapter_AdapterInfo_Get(IntPtr info, int inputSize);
/// <summary> Function to determine if the adapter is active or not.</summary>
/// <remarks>The function is used to check if the adapter associated with iAdapterIndex is active</remarks>
/// <param name="adapterIndex"> Adapter Index.</param>
/// <param name="status"> Status of the adapter. True: Active; False: Dsiabled</param>
/// <returns>Non zero is successfull</returns>
internal delegate int ADL_Adapter_Active_Get(int adapterIndex, ref int status);
/// <summary>Get display information based on adapter index</summary>
/// <param name="adapterIndex">Adapter Index</param>
/// <param name="numDisplays">return the total number of supported displays</param>
/// <param name="displayInfoArray">return ADLDisplayInfo Array for supported displays' information</param>
/// <param name="forceDetect">force detect or not</param>
/// <returns>return ADL Error Code</returns>
internal delegate int ADL_Display_DisplayInfo_Get(int adapterIndex, ref int numDisplays, out IntPtr displayInfoArray, int forceDetect);
internal delegate int ADL_Display_Size_Get(int adapterIndex, int displayIndex, ref int lpWidth, ref int lpHeight, ref int lpDefaultWidth, ref int lpDefaultHeight, ref int lpMinWidth, ref int lpMinHeight, ref int lpMaxWidth, ref int lpMaxHeight, ref int lpStepWidth, ref int lpStepHeight);
internal delegate int ADL_Display_PixelFormat_Get(int adapterIndex, int displayIndex, ref int pixelFormat);
internal delegate int ADL_Display_PixelFormat_Set(int adapterIndex, int displayIndex, int pixelFormat);
internal delegate int ADL_Display_ColorDepth_Get(int adapterIndex, int displayIndex, ref int colorDepth);
internal delegate int ADL_Display_ColorDepth_Set(int adapterIndex, int displayIndex, int colorDepth);
internal delegate int ADL_Display_DitherState_Get(int adapterIndex, int displayIndex, ref int ditherState);
internal delegate int ADL_Display_DitherState_Set(int adapterIndex, int displayIndex, int ditherState);
internal delegate int ADL2_Display_HDRState_Get(ADL_CONTEXT_HANDLE context, int adapterIndex, ADLDisplayID displayID, ref int support, ref int enable);
internal delegate int ADL2_Display_HDRState_Set(ADL_CONTEXT_HANDLE context, int adapterIndex, ADLDisplayID displayID, int enable);
#endregion Export Delegates
#region Class ADLImport
/// <summary> ADLImport class</summary>
private static class ADLImport
@@ -227,31 +243,10 @@ namespace ATI.ADL
internal static extern HMODULE GetModuleHandle (string moduleName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Main_Control_Create (ADL_Main_Memory_Alloc callback, int enumConnectedAdapters);
internal static extern int ADL_Main_Control_IsFunctionValid(HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Main_Control_Destroy ();
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Main_Control_IsFunctionValid (HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern FARPROC ADL_Main_Control_GetProcAddress (HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Adapter_NumberOfAdapters_Get (ref int numAdapters);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Adapter_AdapterInfo_Get (IntPtr info, int inputSize);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Adapter_Active_Get(int adapterIndex, ref int status);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Display_DisplayInfo_Get(int adapterIndex, ref int numDisplays, out IntPtr displayInfoArray, int forceDetect);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Display_Size_Get(int iAdapterIndex, int iDisplayIndex, ref int lpWidth, ref int lpHeight, ref int lpDefaultWidth, ref int lpDefaultHeight, ref int lpMinWidth, ref int lpMinHeight, ref int lpMaxWidth, ref int lpMaxHeight, ref int lpStepWidth, ref int lpStepHeight);
internal static extern FARPROC ADL_Main_Control_GetProcAddress(HMODULE module, string procName);
#endregion DLLImport
}
@@ -291,9 +286,13 @@ namespace ATI.ADL
/// <summary> Destructor to force calling ADL Destroy function before free up the ADL library</summary>
~ADLCheckLibrary ()
{
if (System.IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
ADLImport.ADL_Main_Control_Destroy();
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Destroy>();
if (del != null)
{
del();
}
}
}
#endregion Destructor
@@ -304,8 +303,8 @@ namespace ATI.ADL
/// <returns>return true, if function exists</returns>
internal static bool IsFunctionValid (string functionName)
{
bool result = false;
if (System.IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
var result = false;
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
if (1 == ADLImport.ADL_Main_Control_IsFunctionValid(ADLCheckLibrary_.ADLLibrary, functionName))
{
@@ -322,8 +321,8 @@ namespace ATI.ADL
/// <returns>return function pointer, if function exists</returns>
internal static FARPROC GetProcAddress (string functionName)
{
FARPROC result = System.IntPtr.Zero;
if (System.IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
var result = IntPtr.Zero;
if (IntPtr.Zero != ADLCheckLibrary_.ADLLibrary)
{
result = ADLImport.ADL_Main_Control_GetProcAddress(ADLCheckLibrary_.ADLLibrary, functionName);
}
@@ -337,7 +336,7 @@ namespace ATI.ADL
#region ADL_Main_Memory_Alloc
/// <summary> Build in memory allocation function</summary>
internal static ADL_Main_Memory_Alloc ADL_Main_Memory_Alloc = ADL_Main_Memory_Alloc_;
internal static ADL_Main_Memory_Alloc ADL_Main_Memory_Alloc_Func = ADL_Main_Memory_Alloc_;
/// <summary> Build in memory allocation function</summary>
/// <param name="size">input size</param>
/// <returns>return the memory buffer</returns>
@@ -360,166 +359,29 @@ namespace ATI.ADL
}
#endregion ADL_Main_Memory_Free
#region ADL_Main_Control_Create
/// <summary> ADL_Main_Control_Create Delegates</summary>
internal static ADL_Main_Control_Create ADL_Main_Control_Create
public static T GetDelegate<T>() where T : class
{
get
{
if (!ADL_Main_Control_Create_Check && null == ADL_Main_Control_Create_)
{
ADL_Main_Control_Create_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Main_Control_Create"))
{
ADL_Main_Control_Create_ = ADLImport.ADL_Main_Control_Create;
}
}
return ADL_Main_Control_Create_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Main_Control_Create ADL_Main_Control_Create_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Main_Control_Create_Check = false;
#endregion ADL_Main_Control_Create
var type = typeof(T);
#region ADL_Main_Control_Destroy
/// <summary> ADL_Main_Control_Destroy Delegates</summary>
internal static ADL_Main_Control_Destroy ADL_Main_Control_Destroy
{
get
lock (ADLDelegates)
{
if (!ADL_Main_Control_Destroy_Check && null == ADL_Main_Control_Destroy_)
if (ADLDelegates.ContainsKey(type))
{
ADL_Main_Control_Destroy_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Main_Control_Destroy"))
{
ADL_Main_Control_Destroy_ = ADLImport.ADL_Main_Control_Destroy;
}
return ADLDelegates[type] as T;
}
return ADL_Main_Control_Destroy_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Main_Control_Destroy ADL_Main_Control_Destroy_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Main_Control_Destroy_Check = false;
#endregion ADL_Main_Control_Destroy
#region ADL_Adapter_NumberOfAdapters_Get
/// <summary> ADL_Adapter_NumberOfAdapters_Get Delegates</summary>
internal static ADL_Adapter_NumberOfAdapters_Get ADL_Adapter_NumberOfAdapters_Get
{
get
{
if (!ADL_Adapter_NumberOfAdapters_Get_Check && null == ADL_Adapter_NumberOfAdapters_Get_)
var ptr = ADLCheckLibrary.GetProcAddress(type.Name);
if (ptr != FARPROC.Zero)
{
ADL_Adapter_NumberOfAdapters_Get_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Adapter_NumberOfAdapters_Get"))
{
ADL_Adapter_NumberOfAdapters_Get_ = ADLImport.ADL_Adapter_NumberOfAdapters_Get;
}
}
return ADL_Adapter_NumberOfAdapters_Get_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Adapter_NumberOfAdapters_Get ADL_Adapter_NumberOfAdapters_Get_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Adapter_NumberOfAdapters_Get_Check = false;
#endregion ADL_Adapter_NumberOfAdapters_Get
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, type);
ADLDelegates.Add(type, delegateValue);
#region ADL_Adapter_AdapterInfo_Get
/// <summary> ADL_Adapter_AdapterInfo_Get Delegates</summary>
internal static ADL_Adapter_AdapterInfo_Get ADL_Adapter_AdapterInfo_Get
{
get
{
if (!ADL_Adapter_AdapterInfo_Get_Check && null == ADL_Adapter_AdapterInfo_Get_)
{
ADL_Adapter_AdapterInfo_Get_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Adapter_AdapterInfo_Get"))
{
ADL_Adapter_AdapterInfo_Get_ = ADLImport.ADL_Adapter_AdapterInfo_Get;
}
return delegateValue as T;
}
return ADL_Adapter_AdapterInfo_Get_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Adapter_AdapterInfo_Get ADL_Adapter_AdapterInfo_Get_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Adapter_AdapterInfo_Get_Check = false;
#endregion ADL_Adapter_AdapterInfo_Get
#region ADL_Adapter_Active_Get
/// <summary> ADL_Adapter_Active_Get Delegates</summary>
internal static ADL_Adapter_Active_Get ADL_Adapter_Active_Get
{
get
{
if (!ADL_Adapter_Active_Get_Check && null == ADL_Adapter_Active_Get_)
{
ADL_Adapter_Active_Get_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Adapter_Active_Get"))
{
ADL_Adapter_Active_Get_ = ADLImport.ADL_Adapter_Active_Get;
}
}
return ADL_Adapter_Active_Get_;
}
throw new InvalidProgramException("ADL-function not supported");
}
/// <summary> Private Delegate</summary>
private static ADL_Adapter_Active_Get ADL_Adapter_Active_Get_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Adapter_Active_Get_Check = false;
#endregion ADL_Adapter_Active_Get
#region ADL_Display_DisplayInfo_Get
/// <summary> ADL_Display_DisplayInfo_Get Delegates</summary>
internal static ADL_Display_DisplayInfo_Get ADL_Display_DisplayInfo_Get
{
get
{
if (!ADL_Display_DisplayInfo_Get_Check && null == ADL_Display_DisplayInfo_Get_)
{
ADL_Display_DisplayInfo_Get_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Display_DisplayInfo_Get"))
{
ADL_Display_DisplayInfo_Get_ = ADLImport.ADL_Display_DisplayInfo_Get;
}
}
return ADL_Display_DisplayInfo_Get_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Display_DisplayInfo_Get ADL_Display_DisplayInfo_Get_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Display_DisplayInfo_Get_Check = false;
#endregion ADL_Display_DisplayInfo_Get
#region ADL_Display_Size_Get
/// <summary> ADL_Display_Size_Get Delegates</summary>
internal static ADL_Display_Size_Get ADL_Display_Size_Get
{
get
{
if (!ADL_Display_Size_Get_Check && null == ADL_Display_Size_Get)
{
ADL_Display_Size_Get_Check = true;
if (ADLCheckLibrary.IsFunctionValid("ADL_Display_Size_Get"))
{
ADL_Display_Size_Get_ = ADLImport.ADL_Display_Size_Get;
}
}
return ADL_Display_Size_Get_;
}
}
/// <summary> Private Delegate</summary>
private static ADL_Display_Size_Get ADL_Display_Size_Get_ = null;
/// <summary> check flag to indicate the delegate has been checked</summary>
private static bool ADL_Display_Size_Get_Check = false;
#endregion ADL_Display_Size_Get
#endregion Export Functions
}
+281 -53
View File
@@ -3,28 +3,111 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ADL_CONTEXT_HANDLE = System.IntPtr;
namespace ColorControl
{
enum ADLPixelFormat
{
UNKNOWN = 0,
RGB_FULL_RANGE = 1,
YCRCB444 = 2,
YCRCB422 = 3,
RGB_LIMITED_RANGE = 4,
YCRCB420 = 8
}
enum ADLColorDepth
{
UNKNOWN = 0,
BPC6 = 1,
BPC8 = 2,
BPC10 = 3,
BPC12 = 4,
BPC14 = 5,
BPC16 = 6
}
enum ADLDitherState
{
DISABLED = 0,
DRIVER_DEFAULT = 1,
FM6 = 2,
FM8 = 3,
FM10 = 4,
DITH6 = 5,
DITH8 = 6,
DITH10 = 7,
DITH6_NO_FRAME_RAND = 8,
DITH8_NO_FRAME_RAND = 9,
DITH10_NO_FRAME_RAND = 10,
TRUN6 = 11,
TRUN8 = 12,
TRUN10 = 13,
TRUN8_DITH8 = 14,
TRUN10_DUTH6 = 15,
TRUN10_FM8 = 16,
TRUN10_FM6 = 17,
TRUN10_DITH8_FM6 = 18,
DITH10_FM8 = 19,
DITH10_FM6 = 20,
TRUN8_DITH6 = 21,
TRUN8_FM6 = 22,
DITH8_FM6 = 23
}
class ADLWrapper
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static ADL_CONTEXT_HANDLE context = IntPtr.Zero;
public static bool Initialize()
{
int ADLRet = -1;
if (null != ADL.ADL_Main_Control_Create)
//Logger.Debug("Initialize");
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Create>();
if (del != null)
{
// Second parameter is 1: Get only the present adapters
ADLRet = ADL.ADL_Main_Control_Create(ADL.ADL_Main_Memory_Alloc, 1);
ADLRet = del(ADL.ADL_Main_Memory_Alloc_Func, 1);
CheckError(ADLRet, nameof(ADL.ADL_Main_Control_Create));
}
var del2 = ADL.GetDelegate<ADL.ADL2_Main_Control_Create>();
if (del2 != null)
{
// Second parameter is 1: Get only the present adapters
ADLRet = del2(ADL.ADL_Main_Memory_Alloc_Func, 1, ref context);
CheckError(ADLRet, nameof(ADL.ADL2_Main_Control_Create));
}
return (ADL.ADL_SUCCESS == ADLRet);
}
public static void Uninitialze()
{
if (null != ADL.ADL_Main_Control_Destroy)
if (!ADL.ADLDelegates.Any())
{
ADL.ADL_Main_Control_Destroy();
return;
}
var del = ADL.GetDelegate<ADL.ADL_Main_Control_Destroy>();
if (null != del)
{
del();
}
var del2 = ADL.GetDelegate<ADL.ADL2_Main_Control_Destroy>();
if (null != del2 && context != IntPtr.Zero)
{
del2(context);
}
}
@@ -33,11 +116,13 @@ namespace ColorControl
int ADLRet = -1;
int NumberOfAdapters = 0;
if (null != ADL.ADL_Adapter_NumberOfAdapters_Get)
var delNumberOfAdapters = ADL.GetDelegate<ADL.ADL_Adapter_NumberOfAdapters_Get>();
if (null != delNumberOfAdapters)
{
ADL.ADL_Adapter_NumberOfAdapters_Get(ref NumberOfAdapters);
delNumberOfAdapters(ref NumberOfAdapters);
}
Console.WriteLine("Number Of Adapters: " + NumberOfAdapters.ToString() + "\n");
Logger.Debug("Number Of Adapters: " + NumberOfAdapters.ToString() + "\n");
if (NumberOfAdapters > 0)
{
@@ -45,37 +130,38 @@ namespace ColorControl
ADLAdapterInfoArray OSAdapterInfoData;
OSAdapterInfoData = new ADLAdapterInfoArray();
if (null != ADL.ADL_Adapter_AdapterInfo_Get)
var delAdapterInfo = ADL.GetDelegate<ADL.ADL_Adapter_AdapterInfo_Get>();
if (null != delAdapterInfo)
{
IntPtr AdapterBuffer = IntPtr.Zero;
int size = Marshal.SizeOf(OSAdapterInfoData);
AdapterBuffer = Marshal.AllocCoTaskMem((int)size);
Marshal.StructureToPtr(OSAdapterInfoData, AdapterBuffer, false);
if (null != ADL.ADL_Adapter_AdapterInfo_Get)
ADLRet = delAdapterInfo(AdapterBuffer, size);
if (ADL.ADL_SUCCESS == ADLRet)
{
ADLRet = ADL.ADL_Adapter_AdapterInfo_Get(AdapterBuffer, size);
if (ADL.ADL_SUCCESS == ADLRet)
OSAdapterInfoData = (ADLAdapterInfoArray)Marshal.PtrToStructure(AdapterBuffer, OSAdapterInfoData.GetType());
int IsActive = 0;
var delActive = ADL.GetDelegate<ADL.ADL_Adapter_Active_Get>();
for (int i = 0; i < NumberOfAdapters; i++)
{
OSAdapterInfoData = (ADLAdapterInfoArray)Marshal.PtrToStructure(AdapterBuffer, OSAdapterInfoData.GetType());
int IsActive = 0;
// Check if the adapter is active
if (null != delActive)
ADLRet = delActive(OSAdapterInfoData.ADLAdapterInfo[i].AdapterIndex, ref IsActive);
for (int i = 0; i < NumberOfAdapters; i++)
if (ADL.ADL_SUCCESS == ADLRet)
{
// Check if the adapter is active
if (null != ADL.ADL_Adapter_Active_Get)
ADLRet = ADL.ADL_Adapter_Active_Get(OSAdapterInfoData.ADLAdapterInfo[i].AdapterIndex, ref IsActive);
if (ADL.ADL_SUCCESS == ADLRet)
{
}
}
}
else
{
Console.WriteLine("ADL_Adapter_AdapterInfo_Get() returned error code " + ADLRet.ToString());
}
}
else
{
Logger.Debug("ADL_Adapter_AdapterInfo_Get() returned error code " + ADLRet.ToString());
}
// Release the memory for the AdapterInfo structure
if (IntPtr.Zero != AdapterBuffer)
@@ -88,7 +174,7 @@ namespace ColorControl
return null;
}
public static List<ADLDisplayInfo> GetAllDisplays()
public static List<ADLDisplayInfo> GetAllDisplays(bool connected = true)
{
int ADLRet;
int NumberOfDisplays = 0;
@@ -104,24 +190,28 @@ namespace ColorControl
ADLDisplayInfo oneDisplayInfo = new ADLDisplayInfo();
if (null != ADL.ADL_Display_DisplayInfo_Get)
var del = ADL.GetDelegate<ADL.ADL_Display_DisplayInfo_Get>();
if (null != del)
{
IntPtr DisplayBuffer = IntPtr.Zero;
int j = 0;
// Force the display detection and get the Display Info. Use 0 as last parameter to NOT force detection
ADLRet = ADL.ADL_Display_DisplayInfo_Get(primaryAdapter.AdapterIndex, ref NumberOfDisplays, out DisplayBuffer, 1);
ADLRet = del(primaryAdapter.AdapterIndex, ref NumberOfDisplays, out DisplayBuffer, 1);
if (ADL.ADL_SUCCESS == ADLRet)
{
for (j = 0; j < NumberOfDisplays; j++)
{
oneDisplayInfo = (ADLDisplayInfo)Marshal.PtrToStructure(new IntPtr(DisplayBuffer.ToInt32() + j * Marshal.SizeOf(oneDisplayInfo)), oneDisplayInfo.GetType());
DisplayInfoData.Add(oneDisplayInfo);
oneDisplayInfo = (ADLDisplayInfo)Marshal.PtrToStructure(new IntPtr(DisplayBuffer.ToInt64() + j * Marshal.SizeOf(oneDisplayInfo)), oneDisplayInfo.GetType());
if (!connected || (1 == (oneDisplayInfo.DisplayInfoValue & 1))) {
DisplayInfoData.Add(oneDisplayInfo);
}
}
Console.WriteLine("\nTotal Number of Displays supported: " + NumberOfDisplays.ToString());
Console.WriteLine("\nDispID AdpID Type OutType CnctType Connected Mapped InfoValue DisplayName ");
Logger.Debug("Total Number of Displays supported: " + NumberOfDisplays.ToString());
Logger.Debug("DispID DispPhyId AdpID Type OutType CnctType Connected Mapped InfoValue DisplayName ");
for (j = 0; j < NumberOfDisplays; j++)
for (j = 0; j < DisplayInfoData.Count; j++)
{
int InfoValue = DisplayInfoData[j].DisplayInfoValue;
string StrConnected = (1 == (InfoValue & 1)) ? "Yes" : "No ";
@@ -129,21 +219,21 @@ namespace ColorControl
int AdpID = DisplayInfoData[j].DisplayID.DisplayLogicalAdapterIndex;
string StrAdpID = (AdpID < 0) ? "--" : AdpID.ToString("d2");
Console.WriteLine(DisplayInfoData[j].DisplayID.DisplayLogicalIndex.ToString() + " " +
StrAdpID + " " +
DisplayInfoData[j].DisplayType.ToString() + " " +
DisplayInfoData[j].DisplayOutputType.ToString() + " " +
DisplayInfoData[j].DisplayConnector.ToString() + " " +
StrConnected + " " +
StrMapped + " " +
InfoValue.ToString("x4") + " " +
DisplayInfoData[j].DisplayName.ToString());
Logger.Debug(DisplayInfoData[j].DisplayID.DisplayLogicalIndex.ToString() + " " +
DisplayInfoData[j].DisplayID.DisplayPhysicalIndex.ToString() + " " +
StrAdpID + " " +
DisplayInfoData[j].DisplayType.ToString() + " " +
DisplayInfoData[j].DisplayOutputType.ToString() + " " +
DisplayInfoData[j].DisplayConnector.ToString() + " " +
StrConnected + " " +
StrMapped + " " +
InfoValue.ToString("x4") + " " +
DisplayInfoData[j].DisplayName.ToString());
}
Console.WriteLine();
}
else
{
Console.WriteLine("ADL_Display_DisplayInfo_Get() returned error code " + ADLRet.ToString());
Logger.Debug("ADL_Display_DisplayInfo_Get() returned error code " + ADLRet.ToString());
}
// Release the memory for the DisplayInfo structure
if (IntPtr.Zero != DisplayBuffer)
@@ -152,17 +242,155 @@ namespace ColorControl
return DisplayInfoData;
}
public static bool GetDisplayResolution(string displayName, ref int horizontal, ref int vertical)
private static bool CheckError(int ADLRet, string functionName)
{
var displays = GetAllDisplays();
var display = displays.FirstOrDefault(d => d.DisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase));
if (display.DisplayID.DisplayPhysicalIndex >= 0 && ADL.ADL_Display_Size_Get != null)
{
int lpDefaultWidth, lpDefaultHeight, lpMinWidth, lpMinHeight, lpMaxWidth, lpMaxHeight, lpStepWidth, lpStepHeight;
lpDefaultWidth = lpDefaultHeight = lpMinWidth = lpMinHeight = lpMaxWidth = lpMaxHeight = lpStepWidth = lpStepHeight = 0;
var ADLRet = ADL.ADL_Display_Size_Get(display.DisplayID.DisplayPhysicalAdapterIndex, display.DisplayID.DisplayPhysicalIndex, ref horizontal, ref vertical, ref lpDefaultWidth, ref lpDefaultHeight, ref lpMinWidth, ref lpMinHeight, ref lpMaxWidth, ref lpMaxHeight, ref lpStepWidth, ref lpStepHeight);
var result = ADLRet == ADL.ADL_SUCCESS;
return ADLRet == ADL.ADL_SUCCESS;
if (!result)
{
Logger.Error($"Error calling ADL ({functionName}): {ADLRet}");
}
return result;
}
public static bool GetDisplayResolution(ADLDisplayInfo display, ref int horizontal, ref int vertical)
{
var screen = Screen.PrimaryScreen;
var bounds = screen.Bounds;
horizontal = bounds.Width;
vertical = bounds.Height;
Logger.Debug($"GetDisplayResolution: {horizontal}x{vertical}");
//var del = ADL.GetDelegate<ADL.ADL_Display_Size_Get>();
//if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
//{
// int lpDefaultWidth, lpDefaultHeight, lpMinWidth, lpMinHeight, lpMaxWidth, lpMaxHeight, lpStepWidth, lpStepHeight;
// lpDefaultWidth = lpDefaultHeight = lpMinWidth = lpMinHeight = lpMaxWidth = lpMaxHeight = lpStepWidth = lpStepHeight = 0;
// var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref horizontal, ref vertical, ref lpDefaultWidth, ref lpDefaultHeight, ref lpMinWidth, ref lpMinHeight, ref lpMaxWidth, ref lpMaxHeight, ref lpStepWidth, ref lpStepHeight);
// return CheckError(ADLRet, nameof(ADL.ADL_Display_Size_Get));
//}
return true;
}
public static bool GetDisplayPixelFormat(ADLDisplayInfo display, ref ADLPixelFormat pixelFormat)
{
var del = ADL.GetDelegate<ADL.ADL_Display_PixelFormat_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
int pixelFormatInt = 0;
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref pixelFormatInt);
pixelFormat = (ADLPixelFormat)pixelFormatInt;
return CheckError(ADLRet, nameof(ADL.ADL_Display_PixelFormat_Get));
}
return false;
}
public static bool SetDisplayPixelFormat(ADLDisplayInfo display, ADLPixelFormat pixelFormat)
{
var del = ADL.GetDelegate<ADL.ADL_Display_PixelFormat_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)pixelFormat);
return CheckError(ADLRet, nameof(ADL.ADL_Display_PixelFormat_Set));
}
return false;
}
public static bool GetDisplayColorDepth(ADLDisplayInfo display, ref ADLColorDepth colorDepth)
{
var del = ADL.GetDelegate<ADL.ADL_Display_ColorDepth_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
int colorDepthInt = 0;
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref colorDepthInt);
colorDepth = (ADLColorDepth)colorDepthInt;
return CheckError(ADLRet, nameof(ADL.ADL_Display_ColorDepth_Get));
}
return false;
}
public static bool SetDisplayColorDepth(ADLDisplayInfo display, ADLColorDepth colorDepth)
{
var del = ADL.GetDelegate<ADL.ADL_Display_ColorDepth_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)colorDepth);
return CheckError(ADLRet, nameof(ADL.ADL_Display_ColorDepth_Set));
}
return false;
}
public static bool GetDisplayDitherState(ADLDisplayInfo display, ref ADLDitherState ditherState)
{
var del = ADL.GetDelegate<ADL.ADL_Display_DitherState_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ditherStateInt = 0;
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, ref ditherStateInt);
ditherState = (ADLDitherState)ditherStateInt;
return CheckError(ADLRet, nameof(ADL.ADL_Display_DitherState_Get));
}
return false;
}
public static bool SetDisplayDitherState(ADLDisplayInfo display, ADLDitherState ditherState)
{
var del = ADL.GetDelegate<ADL.ADL_Display_DitherState_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID.DisplayLogicalIndex, (int)ditherState);
return CheckError(ADLRet, nameof(ADL.ADL_Display_DitherState_Set));
}
return false;
}
public static bool GetDisplayHDRState(ADLDisplayInfo display, ref bool supported, ref bool enabled)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_HDRState_Get>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var supportedInt = 0;
var enabledInt = 0;
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID, ref supportedInt, ref enabledInt);
supported = supportedInt != 0;
enabled = enabledInt != 0;
return CheckError(ADLRet, nameof(ADL.ADL2_Display_HDRState_Get));
}
return false;
}
public static bool SetDisplayHDRState(ADLDisplayInfo display, bool enabled)
{
var del = ADL.GetDelegate<ADL.ADL2_Display_HDRState_Set>();
if (display.DisplayID.DisplayPhysicalIndex >= 0 && del != null)
{
var ADLRet = del(context, display.DisplayID.DisplayLogicalAdapterIndex, display.DisplayID, enabled ? 1 : 0);
return CheckError(ADLRet, nameof(ADL.ADL2_Display_HDRState_Set));
}
return false;
}
+21
View File
@@ -0,0 +1,21 @@
using ATI.ADL;
using System.Collections.Generic;
namespace ColorControl
{
class AmdDisplayInfo
{
public ADLDisplayInfo Display { get; }
public List<string> Values { get; }
public string InfoLine { get; }
public AmdDisplayInfo(ADLDisplayInfo display, List<string> values, string infoLine)
{
Display = display;
Values = values;
InfoLine = infoLine;
}
}
}
+48 -91
View File
@@ -1,6 +1,4 @@
using NvAPIWrapper.Display;
using NvAPIWrapper.Native.Display;
using System;
using System;
using System.Collections.Generic;
using System.Text;
@@ -9,22 +7,23 @@ namespace ColorControl
class AmdPreset : PresetBase
{
public bool applyColorData { get; set; }
public ColorData colorData { get; set; }
public ADLColorDepth colorDepth { get; set; }
public ADLPixelFormat pixelFormat { get; set; }
public bool applyHDR { get; set; }
public bool HDREnabled { get; set; }
public bool toggleHDR { get; set; }
public bool applyDithering { get; set; }
public bool ditheringEnabled { get; set; }
public ADLDitherState ditherState { get; set; }
public bool applyRefreshRate { get; set; }
public uint refreshRate { get; set; }
public bool primaryDisplay { get; set; }
public string displayName { get; set; }
public AmdPreset()
public AmdPreset() : base()
{
applyColorData = true;
applyDithering = false;
ditheringEnabled = true;
ditherState = ADLDitherState.DRIVER_DEFAULT;
applyHDR = false;
HDREnabled = false;
toggleHDR = false;
@@ -33,12 +32,6 @@ namespace ColorControl
primaryDisplay = true;
}
public AmdPreset(ColorData colorData) : this()
{
id = GetNewId();
this.colorData = colorData;
}
public AmdPreset(AmdPreset preset): this()
{
id = GetNewId();
@@ -46,15 +39,15 @@ namespace ColorControl
primaryDisplay = preset.primaryDisplay;
displayName = preset.displayName;
var colorData = preset.colorData;
this.colorData = new ColorData(colorData.ColorFormat, dynamicRange: colorData.DynamicRange, colorDepth: colorData.ColorDepth, colorSelectionPolicy: colorData.SelectionPolicy);
applyColorData = preset.applyColorData;
colorDepth = preset.colorDepth;
pixelFormat = preset.pixelFormat;
applyHDR = preset.applyHDR;
HDREnabled = preset.HDREnabled;
toggleHDR = preset.toggleHDR;
applyDithering = preset.applyDithering;
ditheringEnabled = preset.ditheringEnabled;
ditherState = preset.ditherState;
applyRefreshRate = preset.applyRefreshRate;
refreshRate = preset.refreshRate;
}
@@ -69,28 +62,26 @@ namespace ColorControl
public static string[] GetColumnNames()
{
//return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" };
return new[] { "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut" };
return new[] { "Name", "Display|140", "Color settings (BPC, format)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut", "Apply on startup" };
}
public List<string> GetDisplayValues()
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>();
values.Add(name);
var display = string.Format("{0}", primaryDisplay ? "Primary" : displayName);
values.Add(display);
var colorSettings = string.Format("{0}: {1}, {2}, {3}, {4}", applyColorData ? "Included" : "Excluded", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry);
var colorSettings = string.Format("{0}: {1}, {2}", applyColorData ? "Included" : "Excluded", colorDepth, pixelFormat);
values.Add(colorSettings);
values.Add(string.Format("{0}: {1}Hz", applyRefreshRate ? "Included" : "Excluded", refreshRate));
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", ditheringEnabled ? "Enabled" : "Disabled"));
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", ditherState));
values.Add(string.Format("{0}: {1}", applyHDR ? "Included" : "Excluded", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled"));
//values.Add(colorData.ColorDepth.ToString());
//values.Add(colorData.ColorFormat.ToString());
//values.Add(colorData.DynamicRange.ToString());
//values.Add(toggleHDR.ToString());
values.Add(shortcut);
values.Add(string.Format("{0}", config?.AmdPresetId_ApplyOnStartup == id ? "Yes" : string.Empty));
return values;
}
@@ -99,21 +90,11 @@ namespace ColorControl
{
var presets = new List<AmdPreset>();
presets.Add(new AmdPreset(new ColorData(ColorDataFormat.Auto, dynamicRange: ColorDataDynamicRange.Auto, colorDepth: ColorDataDepth.Default, colorSelectionPolicy: ColorDataSelectionPolicy.BestQuality)));
var preset = new AmdPreset();
preset.colorDepth = ADLColorDepth.BPC8;
preset.pixelFormat = ADLPixelFormat.RGB_FULL_RANGE;
for (var dynamicRange = ColorDataDynamicRange.VESA; dynamicRange <= ColorDataDynamicRange.CEA; dynamicRange++)
{
presets.Add(new AmdPreset(new ColorData(ColorDataFormat.RGB, dynamicRange: dynamicRange, colorDepth: ColorDataDepth.BPC8, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
presets.Add(new AmdPreset(new ColorData(ColorDataFormat.RGB, dynamicRange: dynamicRange, colorDepth: ColorDataDepth.BPC10, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
presets.Add(new AmdPreset(new ColorData(ColorDataFormat.RGB, dynamicRange: dynamicRange, colorDepth: ColorDataDepth.BPC12, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
}
for (var format = ColorDataFormat.YUV422; format <= ColorDataFormat.YUV420; format++)
{
presets.Add(new AmdPreset(new ColorData(format, dynamicRange: ColorDataDynamicRange.Auto, colorDepth: ColorDataDepth.BPC8, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
presets.Add(new AmdPreset(new ColorData(format, dynamicRange: ColorDataDynamicRange.Auto, colorDepth: ColorDataDepth.BPC10, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
presets.Add(new AmdPreset(new ColorData(format, dynamicRange: ColorDataDynamicRange.Auto, colorDepth: ColorDataDepth.BPC12, colorSelectionPolicy: ColorDataSelectionPolicy.User)));
}
presets.Add(preset);
return presets;
}
@@ -121,66 +102,42 @@ namespace ColorControl
public string GetTextForMenuItem()
{
var sb = new StringBuilder();
if (displayName != null)
if (!string.IsNullOrEmpty(name))
{
sb.AppendFormat("Display: {0} / ", displayName);
}
if (applyColorData)
{
var colorSettings = string.Format("Format: {0}, {1}, {2}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange);
sb.Append(colorSettings);
sb.Append(name);
sb.Append(" / ");
}
if (applyRefreshRate)
else
{
sb.AppendFormat("{0}Hz", refreshRate);
sb.Append(" / ");
}
if (applyDithering)
{
sb.AppendFormat("Dithering: {0}", ditheringEnabled ? "Yes" : "No");
sb.Append(" / ");
}
if (applyHDR)
{
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
sb.Append(" / ");
if (displayName != null)
{
sb.AppendFormat("Display: {0} / ", displayName);
}
if (applyColorData)
{
var colorSettings = string.Format("Format: {0}, {1}", colorDepth, pixelFormat);
sb.Append(colorSettings);
sb.Append(" / ");
}
if (applyRefreshRate)
{
sb.AppendFormat("{0}Hz", refreshRate);
sb.Append(" / ");
}
if (applyDithering)
{
sb.AppendFormat("Dithering: {0}", ditherState);
sb.Append(" / ");
}
if (applyHDR)
{
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
sb.Append(" / ");
}
}
return sb.ToString(0, Math.Max(0, sb.Length - 3));
}
public static ColorData GenerateColorData(IDictionary<string, object> dictionary)
{
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);
}
}
}
+276 -47
View File
@@ -1,86 +1,223 @@
//using ATI.ADL;
using ATI.ADL;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace ColorControl
{
class AmdService : GraphicsService
class AmdService : GraphicsService<AmdPreset>
{
private string CurrentDisplay;
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public AmdService()
private ADLDisplayInfo _currentDisplay;
public AmdService(string dataPath) : base(dataPath, "AmdPresets.json")
{
LoadPresets();
}
public static bool ExecutePresetAsync(string idOrName)
{
try
{
var service = new AmdService(Program.DataDir);
var result = service.ApplyPreset(idOrName);
if (!result)
{
Console.WriteLine("Preset not found or error while executing.");
}
return result;
}
catch (Exception ex)
{
Console.WriteLine("Error executing preset: " + ex.ToLogString());
return false;
}
}
protected override List<AmdPreset> GetDefaultPresets()
{
return AmdPreset.GetDefaultPresets();
}
private void SavePresets()
{
try
{
var json = _JsonSerializer.Serialize(_presets);
File.WriteAllText(_presetsFilename, json);
}
catch (Exception e)
{
Logger.Error(e.ToLogString());
}
}
public void GlobalSave()
{
SavePresets();
}
public override bool HasDisplaysAttached()
{
try
{
return GetDisplays().Any();
}
catch (Exception)
{
return false;
}
}
public void SetCurrentDisplay(AmdPreset preset)
{
CurrentDisplay = preset.displayName;
var displays = GetDisplays();
if (preset.primaryDisplay)
{
_currentDisplay = displays.FirstOrDefault();
}
else
{
_currentDisplay = displays.FirstOrDefault(x => x.DisplayName.Equals(preset.displayName));
}
}
public string GetCurrentDisplay()
public ADLDisplayInfo GetCurrentDisplay()
{
return CurrentDisplay;
if (string.IsNullOrEmpty(_currentDisplay.DisplayName))
{
_currentDisplay = GetDisplays().FirstOrDefault();
}
return _currentDisplay;
}
public bool ApplyPreset(AmdPreset preset, Config config)
public bool ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return ApplyPreset(preset, Program.AppContext);
}
else
{
return false;
}
}
public bool ApplyPreset(AmdPreset preset, AppContext appContext)
{
SetCurrentDisplay(preset);
if (string.IsNullOrEmpty(_currentDisplay.DisplayName))
{
Logger.Error("Could not set current display");
return false;
}
var result = true;
if (preset.applyHDR)
{
if (preset.toggleHDR || preset.HDREnabled != IsHDREnabled())
var hdrEnabled = IsHDREnabled();
if (preset.toggleHDR)
{
ToggleHDR(config.DisplaySettingsDelay);
result = ADLWrapper.SetDisplayHDRState(_currentDisplay, !hdrEnabled);
}
else
{
result = ADLWrapper.SetDisplayHDRState(_currentDisplay, preset.HDREnabled);
}
//if (preset.toggleHDR || preset.HDREnabled != hdrEnabled)
//{
// ToggleHDR(config.DisplaySettingsDelay);
//}
}
if (preset.applyRefreshRate)
{
SetRefreshRate(preset.refreshRate);
if (!SetRefreshRate(preset.refreshRate))
{
result = false;
}
}
if (preset.applyColorData)
{
var display = GetCurrentDisplay();
// TODO: ADL call to set color format, depth, space
// ADL_Display_PixelFormat_Set
// ADL_Display_ColorDepth_Set
//var data = new ColorData(ColorDataFormat.YUV444, ColorDataColorimetry.YCC709, ColorDataDynamicRange.Auto, ColorDataDepth.BPC8, preset.colorData.SelectionPolicy, ColorDataDesktopDepth.Default);
//display.DisplayDevice.SetColorData(preset.colorData);
if (!ADLWrapper.SetDisplayColorDepth(_currentDisplay, preset.colorDepth))
{
Logger.Error("Could not set color depth");
result = false;
}
if (preset.pixelFormat != ADLPixelFormat.UNKNOWN && !ADLWrapper.SetDisplayPixelFormat(_currentDisplay, preset.pixelFormat))
{
Logger.Error("Could not set pixel format");
result = false;
}
}
if (preset.applyDithering)
{
SetDithering(preset.ditheringEnabled);
if (!SetDithering(preset.ditherState))
{
result = false;
}
}
return true;
_lastAppliedPreset = preset;
PresetApplied();
return result;
}
public void SetDithering(bool enabled)
public bool SetDithering(ADLDitherState ditherState)
{
// TODO: ADL call to set dithering state
// ADL_Display_DitherState_Get
var display = GetCurrentDisplay();
return ADLWrapper.SetDisplayDitherState(display, ditherState);
}
public bool GetDithering()
public ADLDitherState GetDithering(ADLDisplayInfo display = default(ADLDisplayInfo))
{
// TODO: ADL call to get dithering state
// ADL_Display_DitherState_Set
return false;
if (string.IsNullOrEmpty(display.DisplayName))
{
display = GetCurrentDisplay();
}
var ditherState = ADLDitherState.DRIVER_DEFAULT;
if (ADLWrapper.GetDisplayDitherState(display, ref ditherState))
{
return ditherState;
}
return ditherState;
}
public string GetDisplayDeviceName(ADLDisplayInfo display)
{
return Screen.PrimaryScreen.DeviceName;
}
public bool SetRefreshRate(uint refreshRate)
{
var portrait = false;
var horizontal = 3840;
var vertical = 2160;
var display = GetCurrentDisplay();
//ADLWrapper.GetDisplayResolution(display, ref horizontal, ref vertical);
ADLWrapper.GetDisplayResolution(display, ref horizontal, ref vertical);
return SetRefreshRateInternal(display, refreshRate, portrait, horizontal, vertical);
var portrait = vertical > horizontal;
return SetRefreshRateInternal(GetDisplayDeviceName(display), refreshRate, portrait, horizontal, vertical);
}
public List<uint> GetAvailableRefreshRates(AmdPreset preset = null)
@@ -90,43 +227,135 @@ namespace ColorControl
SetCurrentDisplay(preset);
}
var portrait = false;
var horizontal = 3840;
var vertical = 2160;
var display = GetCurrentDisplay();
//ADLWrapper.GetDisplayResolution(display, ref horizontal, ref vertical);
ADLWrapper.GetDisplayResolution(display, ref horizontal, ref vertical);
return GetAvailableRefreshRatesInternal(display, portrait, horizontal, vertical);
var portrait = vertical > horizontal;
return GetAvailableRefreshRatesInternal(GetDisplayDeviceName(display), portrait, horizontal, vertical);
}
public bool IsHDREnabled()
{
// TODO: ADL call to get HDR state
//var displayDevice = GetCurrentDisplay().DisplayDevice;
//var hdr = displayDevice.HDRColorData;
//return hdr?.HDRMode == ColorDataHDRMode.UHDA;
return false;
var display = GetCurrentDisplay();
var supported = false;
var enabled = false;
ADLWrapper.GetDisplayHDRState(display, ref supported, ref enabled);
Logger.Debug($"IsHDREnabled: supported {supported}, enabled {enabled}");
return enabled;
}
public List<string> GetDisplays()
public List<ADLDisplayInfo> GetDisplays()
{
//return ADLWrapper.GetAllDisplays();
return null;
var displays = ADLWrapper.GetAllDisplays();
//if (!displays.Any())
//{
// displays.Add(new ADLDisplayInfo
// {
// DisplayName = "Dummy"
// });
//}
return displays;
}
public string GetFullDisplayName(ADLDisplayInfo display)
{
var name = display.DisplayName;
var screen = GetScreenForDisplay(display);
if (screen != null)
{
name = $"{screen.DeviceName} ({name})";
}
return name;
}
public Screen GetScreenForDisplay(ADLDisplayInfo display)
{
var index = 0;
foreach (var screen in Screen.AllScreens)
{
Logger.Debug($"SCREEN[{index}]: {screen.DeviceName}, {screen.DeviceFriendlyName()}, {screen.Primary}");
index++;
}
return Screen.AllScreens.FirstOrDefault(x => x.DeviceFriendlyName().Equals(display.DisplayName));
}
public List<AmdDisplayInfo> GetDisplayInfos()
{
List<ADLDisplayInfo> displays;
try
{
displays = GetDisplays();
}
catch (Exception e)
{
Logger.Error("Error while getting displays: " + e.ToLogString());
return null;
}
var list = new List<AmdDisplayInfo>();
foreach (var display in displays)
{
var values = new List<string>();
values.Add("Current settings");
var screen = GetScreenForDisplay(display);
var name = GetFullDisplayName(display);
values.Add(name);
var colorDepth = ADLColorDepth.UNKNOWN;
ADLWrapper.GetDisplayColorDepth(display, ref colorDepth);
var pixelFormat = ADLPixelFormat.UNKNOWN;
ADLWrapper.GetDisplayPixelFormat(display, ref pixelFormat);
var colorSettings = $"{colorDepth}, {pixelFormat}";
values.Add(colorSettings);
var refreshRate = GetCurrentRefreshRate(screen?.DeviceName);
values.Add($"{refreshRate}Hz");
var ditherState = GetDithering(display);
values.Add(ditherState.ToString());
var hdrEnabled = IsHDREnabled();
values.Add(hdrEnabled ? "Yes" : "No");
var infoLine = string.Format("{0}: {1}, {2}Hz, HDR: {3}", name, colorSettings, refreshRate, hdrEnabled ? "Yes" : "No");
var displayInfo = new AmdDisplayInfo(display, values, infoLine);
list.Add(displayInfo);
}
return list;
}
protected override void Initialize()
{
//if (!ADLWrapper.Initialize())
//{
// throw new InvalidOperationException("Cannot load AMD API");
//}
throw new InvalidOperationException("Cannot load AMD API");
if (!ADLWrapper.Initialize())
{
throw new InvalidOperationException("Cannot load AMD API");
}
}
protected override void Uninitialize()
{
//ADLWrapper.Uninitialze();
ADLWrapper.Uninitialze();
}
}
}
+6 -2
View File
@@ -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" />
@@ -15,7 +15,11 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="PacketDotNet" publicKeyToken="451414c7667b2a58" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
+19
View File
@@ -0,0 +1,19 @@
namespace ColorControl
{
public class AppContext
{
public static AppContext CurrentContext { get; private set; }
public Config Config { get; private set; }
public StartUpParams StartUpParams { get; private set; }
public AppContext(Config config, StartUpParams startUpParams)
{
Config = config;
StartUpParams = startUpParams;
CurrentContext = this;
}
}
}
+564
View File
@@ -0,0 +1,564 @@
using NWin32;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ColorControl
{
// This class takes care of wrapping "Connecting and Configuring Displays(CCD) Win32 API"
internal static class CCD
{
public enum DisplayTopology
{
Internal,
External,
Extend,
Clone
}
public static DisplayTopology GetDisplayTopology()
{
uint numPathArrayElements;
uint numModeInfoArrayElements;
NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.DatabaseCurrent, out numPathArrayElements, out numModeInfoArrayElements);
var pathArray = new DisplayConfigPathInfo[numPathArrayElements];
var modeArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
DisplayConfigTopologyId displayTopology;
NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref numPathArrayElements, pathArray, ref numModeInfoArrayElements, modeArray, out displayTopology);
switch (displayTopology)
{
case DisplayConfigTopologyId.External: return DisplayTopology.External;
case DisplayConfigTopologyId.Internal: return DisplayTopology.Internal;
case DisplayConfigTopologyId.Extend: return DisplayTopology.Extend;
}
return DisplayTopology.Clone;
}
public static void SetDisplayTopology(DisplayTopology displayTopology)
{
switch (displayTopology)
{
case DisplayTopology.External:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExternal));
break;
case DisplayTopology.Internal:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyInternal));
break;
case DisplayTopology.Extend:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyExtend));
break;
case DisplayTopology.Clone:
NativeMethods.SetDisplayConfig(0, null, 0, null, (SdcFlags.Apply | SdcFlags.TopologyClone));
break;
}
}
public static void SetHDRState(bool enabled, string displayName = null)
{
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
if (err == NativeConstants.ERROR_SUCCESS)
{
var pathsArray = new DisplayConfigPathInfo[pathCount];
var modesArray = new DisplayConfigModeInfo[modeCount];
DisplayConfigTopologyId displayTopology;
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
if (err == NativeConstants.ERROR_SUCCESS)
{
foreach (var path in pathsArray)
{
// get display name
var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
info.header.size = Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
info.header.adapterId = path.sourceInfo.adapterId;
info.header.id = path.sourceInfo.id;
err = NativeMethods.DisplayConfigGetDeviceInfo(ref info);
if (err != NativeConstants.ERROR_SUCCESS)
{
break;
}
var deviceName = info.viewGdiDeviceName;
if (displayName != null && !EqualDisplayNames(deviceName, displayName))
{
continue;
}
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>(); ;
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>(); ;
for (int i = 0; i < modeCount; i++)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
{
setpacket.header.adapterId = modesArray[i].adapterId;
setpacket.header.id = modesArray[i].id;
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS)
{
if (requestpacket.advancedColorSupported)
{
setpacket.enableAdvancedColor = enabled ? 1U : 0;
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
}
}
}
}
}
}
}
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
}
private static bool EqualDisplayNames(string displayName1, string displayName2)
{
displayName1 = displayName1.Replace("\\", string.Empty);
displayName2 = displayName2.Replace("\\", string.Empty);
return displayName1.Equals(displayName2, StringComparison.OrdinalIgnoreCase);
}
private enum DISPLAYCONFIG_MODE_INFO_TYPE
{
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2,
DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3,
}
private enum DISPLAYCONFIG_DEVICE_INFO_TYPE
{
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
public DISPLAYCONFIG_DEVICE_INFO_TYPE type;
public int size;
public LUID adapterId;
public uint id;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
uint LowPart;
uint HighPart;
}
[Flags]
enum DisplayConfigVideoOutputTechnology : uint
{
Other = 4294967295, // -1
Hd15 = 0,
Svideo = 1,
CompositeVideo = 2,
ComponentVideo = 3,
Dvi = 4,
Hdmi = 5,
Lvds = 6,
DJpn = 8,
Sdi = 9,
DisplayportExternal = 10,
DisplayportEmbedded = 11,
UdiExternal = 12,
UdiEmbedded = 13,
Sdtvdongle = 14,
Internal = 0x80000000,
ForceUint32 = 0xFFFFFFFF
}
#region SdcFlags enum
[Flags]
enum SdcFlags : uint
{
Zero = 0,
TopologyInternal = 0x00000001,
TopologyClone = 0x00000002,
TopologyExtend = 0x00000004,
TopologyExternal = 0x00000008,
TopologySupplied = 0x00000010,
UseSuppliedDisplayConfig = 0x00000020,
Validate = 0x00000040,
Apply = 0x00000080,
NoOptimization = 0x00000100,
SaveToDatabase = 0x00000200,
AllowChanges = 0x00000400,
PathPersistIfRequired = 0x00000800,
ForceModeEnumeration = 0x00001000,
AllowPathOrderChanges = 0x00002000,
UseDatabaseCurrent = TopologyInternal | TopologyClone | TopologyExtend | TopologyExternal
}
[Flags]
enum DisplayConfigFlags : uint
{
Zero = 0x0,
PathActive = 0x00000001
}
[Flags]
enum DisplayConfigSourceStatus
{
Zero = 0x0,
InUse = 0x00000001
}
[Flags]
enum DisplayConfigTargetStatus : uint
{
Zero = 0x0,
InUse = 0x00000001,
FORCIBLE = 0x00000002,
ForcedAvailabilityBoot = 0x00000004,
ForcedAvailabilityPath = 0x00000008,
ForcedAvailabilitySystem = 0x00000010,
}
[Flags]
enum DisplayConfigRotation : uint
{
Zero = 0x0,
Identity = 1,
Rotate90 = 2,
Rotate180 = 3,
Rotate270 = 4,
ForceUint32 = 0xFFFFFFFF
}
[Flags]
enum DisplayConfigPixelFormat : uint
{
Zero = 0x0,
Pixelformat8Bpp = 1,
Pixelformat16Bpp = 2,
Pixelformat24Bpp = 3,
Pixelformat32Bpp = 4,
PixelformatNongdi = 5,
PixelformatForceUint32 = 0xffffffff
}
[Flags]
enum DisplayConfigScaling : uint
{
Zero = 0x0,
Identity = 1,
Centered = 2,
Stretched = 3,
Aspectratiocenteredmax = 4,
Custom = 5,
Preferred = 128,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigRational
{
uint numerator;
uint denominator;
}
[Flags]
enum DisplayConfigScanLineOrdering : uint
{
Unspecified = 0,
Progressive = 1,
Interlaced = 2,
InterlacedUpperfieldfirst = Interlaced,
InterlacedLowerfieldfirst = 3,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathInfo
{
public DisplayConfigPathSourceInfo sourceInfo;
public DisplayConfigPathTargetInfo targetInfo;
public uint flags;
}
[Flags]
enum DisplayConfigModeInfoType : uint
{
Zero = 0,
Source = 1,
Target = 2,
ForceUint32 = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Explicit)]
struct DisplayConfigModeInfo
{
[FieldOffset(0)]
public DisplayConfigModeInfoType infoType;
[FieldOffset(4)]
public uint id;
[FieldOffset(8)]
public LUID adapterId;
[FieldOffset(16)]
public DisplayConfigTargetMode targetMode;
[FieldOffset(16)]
public DisplayConfigSourceMode sourceMode;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfig2DRegion
{
uint cx;
uint cy;
}
[Flags]
enum D3DmdtVideoSignalStandard : uint
{
Uninitialized = 0,
VesaDmt = 1,
VesaGtf = 2,
VesaCvt = 3,
Ibm = 4,
Apple = 5,
NtscM = 6,
NtscJ = 7,
Ntsc443 = 8,
PalB = 9,
PalB1 = 10,
PalG = 11,
PalH = 12,
PalI = 13,
PalD = 14,
PalN = 15,
PalNc = 16,
SecamB = 17,
SecamD = 18,
SecamG = 19,
SecamH = 20,
SecamK = 21,
SecamK1 = 22,
SecamL = 23,
SecamL1 = 24,
Eia861 = 25,
Eia861A = 26,
Eia861B = 27,
PalK = 28,
PalK1 = 29,
PalL = 30,
PalM = 31,
Other = 255
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigVideoSignalInfo
{
long pixelRate;
DisplayConfigRational hSyncFreq;
DisplayConfigRational vSyncFreq;
DisplayConfig2DRegion activeSize;
DisplayConfig2DRegion totalSize;
D3DmdtVideoSignalStandard videoStandard;
DisplayConfigScanLineOrdering ScanLineOrdering;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigTargetMode
{
DisplayConfigVideoSignalInfo targetVideoSignalInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct PointL
{
int x;
int y;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigSourceMode
{
uint width;
uint height;
DisplayConfigPixelFormat pixelFormat;
PointL position;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathSourceInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigSourceStatus statusFlags;
}
[StructLayout(LayoutKind.Sequential)]
struct DisplayConfigPathTargetInfo
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DisplayConfigVideoOutputTechnology outputTechnology;
public DisplayConfigRotation rotation;
public DisplayConfigScaling scaling;
public DisplayConfigRational refreshRate;
public DisplayConfigScanLineOrdering scanLineOrdering;
public bool targetAvailable;
public DisplayConfigTargetStatus statusFlags;
}
[Flags]
enum QueryDisplayFlags : uint
{
Zero = 0x0,
AllPaths = 0x00000001,
OnlyActivePaths = 0x00000002,
DatabaseCurrent = 0x00000004
}
[Flags]
enum DisplayConfigTopologyId : uint
{
Zero = 0x0,
Internal = 0x00000001,
Clone = 0x00000002,
Extend = 0x00000004,
External = 0x00000008,
ForceUint32 = 0xFFFFFFFF
}
private enum DISPLAYCONFIG_COLOR_ENCODING
{
DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3,
DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint value;
public DISPLAYCONFIG_COLOR_ENCODING colorEncoding;
public int bitsPerColorChannel;
public bool advancedColorSupported => (value & 0x1) == 0x1;
public bool advancedColorEnabled => (value & 0x2) == 0x2;
public bool wideColorEnforced => (value & 0x4) == 0x4;
public bool advancedColorForceDisabled => (value & 0x8) == 0x8;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint enableAdvancedColor;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS
{
public uint value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_TARGET_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
public DisplayConfigVideoOutputTechnology outputTechnology;
public ushort edidManufactureId;
public ushort edidProductCodeId;
public uint connectorInstance;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string monitorFriendlyDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string monitorDevicePat;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string viewGdiDeviceName;
}
#endregion
static class NativeMethods
{
[DllImport("User32.dll")]
public static extern int GetDisplayConfigBufferSizes(QueryDisplayFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
[DllImport("User32.dll")]
public static extern int SetDisplayConfig(uint numPathArrayElements, [In] DisplayConfigPathInfo[] pathArray, uint numModeInfoArrayElements, [In] DisplayConfigModeInfo[] modeInfoArray, SdcFlags flags);
[DllImport("User32.dll")]
public static extern int QueryDisplayConfig(QueryDisplayFlags flags, ref uint numPathArrayElements, [In, Out] DisplayConfigPathInfo[] pathInfoArray, ref uint modeInfoArrayElements, [In, Out] DisplayConfigModeInfo[] modeInfoArray, out DisplayConfigTopologyId id);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO setPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);
}
}
}
+63 -27
View File
@@ -27,7 +27,7 @@
<ProductName>ColorControl</ProductName>
<PublisherName>Maassoft</PublisherName>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.3.6.0</ApplicationVersion>
<ApplicationVersion>4.2.3.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>
@@ -101,33 +101,33 @@
<ApplicationIcon>pngbarn.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.8.20.0, Culture=neutral, PublicKeyToken=c416bc1b32d97233, processorArchitecture=MSIL">
<HintPath>..\packages\TaskScheduler.2.8.20\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
<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=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<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.2\lib\net45\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.7.10\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="NStandard, Version=0.4.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NStandard.0.4.3\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.0.98, Culture=neutral, PublicKeyToken=310fd07b25df79b3, processorArchitecture=MSIL">
<HintPath>..\packages\NvAPIWrapper.Net.0.8.0.98\lib\net45\NvAPIWrapper.dll</HintPath>
<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.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NWin32.1.0.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.0.5.0, Culture=neutral, PublicKeyToken=451414c7667b2a58, processorArchitecture=MSIL">
<HintPath>..\packages\PacketDotNet.1.0.5\lib\net45\PacketDotNet.dll</HintPath>
<Reference Include="PacketDotNet, Version=1.2.0.0, Culture=neutral, PublicKeyToken=451414c7667b2a58, processorArchitecture=MSIL">
<HintPath>..\packages\PacketDotNet.1.2.0\lib\net45\PacketDotNet.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="SharpPcap, Version=5.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SharpPcap.5.2.0\lib\netstandard2.0\SharpPcap.dll</HintPath>
<Reference Include="SharpPcap, Version=5.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SharpPcap.5.4.0\lib\netstandard2.0\SharpPcap.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
@@ -137,15 +137,19 @@
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Management" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Formatting, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.7.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
@@ -172,9 +176,36 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="ADL.cs" />
<Compile Include="ADLWrapper.cs" />
<Compile Include="AmdDisplayInfo.cs" />
<Compile Include="AppContext.cs" />
<Compile Include="CCD.cs" />
<Compile Include="LgDevice.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="RemoteControlPanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="RemoteControlPanel.Designer.cs">
<DependentUpon>RemoteControlPanel.cs</DependentUpon>
</Compile>
<Compile Include="UserSessionInfo.cs" />
<Compile Include="ServiceBase.cs" />
<Compile Include="NvDisplayInfo.cs" />
<Compile Include="MessageForms.cs" />
<Compile Include="GraphicsService.cs" />
<Compile Include="AmdService.cs" />
<Compile Include="AmdPreset.cs" />
<Compile Include="RemoteControlForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="RemoteControlForm.Designer.cs">
<DependentUpon>RemoteControlForm.cs</DependentUpon>
</Compile>
<Compile Include="StartUpParams.cs" />
<Compile Include="LgServiceConfig.cs" />
<Compile Include="WOL.cs" />
@@ -211,18 +242,23 @@
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Resources\RC_coords.txt" />
<None Include="ColorControl_1_TemporaryKey.pfx" />
<None Include="Resources\LG_remote.jpg" />
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="RemoteControlForm.resx">
<DependentUpon>RemoteControlForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="RemoteControlPanel.resx">
<DependentUpon>RemoteControlPanel.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="ColorControl_TemporaryKey.pfx" />
<Content Include="LgPresets.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+1 -28
View File
@@ -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);
}
}
}
+14 -1
View File
@@ -1,11 +1,15 @@
namespace ColorControl
{
class Config
public class Config
{
public bool StartMinimized { get; set; }
public bool MinimizeOnClose { get; set; }
public bool MinimizeToTray { get; set; }
public bool CheckForUpdates { get; set; }
public int DisplaySettingsDelay { get; set; }
public string ScreenSaverShortcut { get; set; }
@@ -13,12 +17,21 @@
public int FormWidth { get; set; }
public int FormHeight { get; set; }
public int NvPresetId_ApplyOnStartup { get; set; }
public int AmdPresetId_ApplyOnStartup { get; set; }
public bool FixChromeFonts { get; set; }
public Config()
{
DisplaySettingsDelay = 1000;
ScreenSaverShortcut = string.Empty;
FormWidth = 900;
FormHeight = 600;
MinimizeToTray = true;
CheckForUpdates = true;
FixChromeFonts = false;
}
}
}
+10 -3
View File
@@ -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))
+26 -12
View File
@@ -9,11 +9,11 @@ using System.Threading;
namespace ColorControl
{
class GraphicsService
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase
{
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public GraphicsService()
public GraphicsService(string dataPath, string presetsBaseFilename) : base(dataPath, presetsBaseFilename)
{
Initialize();
}
@@ -23,15 +23,7 @@ namespace ColorControl
Uninitialize();
}
protected virtual void Initialize()
{
}
protected virtual void Uninitialize()
{
}
public abstract bool HasDisplaysAttached();
protected void ToggleHDR(int delay = 1000)
{
@@ -48,6 +40,18 @@ namespace ColorControl
}
}
protected void OpenDisplaySettings(int delay = 1000)
{
Process.Start("ms-settings:display");
Thread.Sleep(delay);
var process = Process.GetProcessesByName("SystemSettings").FirstOrDefault();
if (process != null)
{
System.Windows.Forms.SendKeys.SendWait("%{F4}");
}
}
protected bool SetRefreshRateInternal(string displayName, uint refreshRate, bool portrait, int horizontal, int vertical)
{
uint i = 0;
@@ -93,5 +97,15 @@ namespace ColorControl
return list;
}
protected uint GetCurrentRefreshRate(string displayName)
{
DEVMODEA devMode;
// NativeMethods defines modeNum as an 'uint' but we need to pass '-1'
if (Utils.EnumDisplaySettingsA(displayName, Utils.ENUM_CURRENT_SETTINGS, out devMode))
{
return devMode.dmDisplayFrequency;
}
return 0;
}
}
}
+670
View File
@@ -0,0 +1,670 @@
using LgTv;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ColorControl
{
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
}
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; }
public string MacAddress { get; private set; }
public bool IsCustom { get; private set; }
[JsonIgnore]
public bool IsDummy { get; private set; }
public bool PowerOnAfterStartup { get; set; }
public bool PowerOnAfterResume { get; set; }
public bool PowerOffOnShutdown { get; set; }
public bool PowerOffOnStandby { get; set; }
public bool PowerSwitchOnScreenSaver { get; set; }
[JsonIgnore]
public PowerState CurrentState { get; set; }
[JsonIgnore]
private LgTvApi _lgTvApi;
[JsonIgnore]
private bool _justWokeUp;
[JsonIgnore]
public PowerOffSource PoweredOffBy { get; private set; }
[JsonIgnore]
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;
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("adjustingLuminance", minValue: -50, maxValue: 50);
}
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)}";
}
public async Task<bool> Connect(int retries = 3)
{
var locked = _connectSemaphore.CurrentCount == 0;
await _connectSemaphore.WaitAsync();
try
{
if (locked && _lgTvApi != null)
{
return true;
}
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;
}
}
finally
{
_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;
}
internal void DisposeConnection()
{
if (_lgTvApi != null)
{
_lgTvApi.Dispose();
_lgTvApi = null;
}
}
public async Task<bool> ExecutePreset(LgPreset preset, bool reconnect = false)
{
var hasApp = !string.IsNullOrEmpty(preset.appId);
var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
if (hasWOL)
{
var connected = await WakeAndConnect(0);
if (!connected)
{
return false;
}
}
for (var tries = 0; tries <= 1; tries++)
{
if (!await Connected(reconnect || tries == 1))
{
return false;
}
if (hasApp)
{
try
{
await _lgTvApi.LaunchApp(preset.appId);
}
catch (Exception ex)
{
string logMessage = ex.ToLogString(Environment.StackTrace);
Logger.Error("Error while launching app: " + logMessage);
if (tries == 0)
{
continue;
}
return false;
}
if (_justWokeUp)
{
_justWokeUp = false;
await Task.Delay(1000);
}
}
if (preset.steps.Any())
{
if (hasApp)
{
await Task.Delay(1500);
}
try
{
await ExecuteSteps(_lgTvApi, preset);
}
catch (Exception ex)
{
string logMessage = ex.ToLogString(Environment.StackTrace);
Logger.Error("Error while executing steps: " + logMessage);
if (tries == 0)
{
continue;
}
return false;
}
}
return true;
}
return true;
}
private async Task ExecuteSteps(LgTvApi api, LgPreset preset)
{
var mouse = await api.GetMouse();
foreach (var step in preset.steps)
{
var keySpec = step.Split(':');
var key = keySpec[0].ToUpper();
var index = key.IndexOf("(");
string[] parameters = null;
if (index > -1)
{
var keyValue = keySpec[0].Split('(');
key = keyValue[0];
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
}
var action = _invokableActions.FirstOrDefault(a => a.Name.Equals(key, StringComparison.OrdinalIgnoreCase));
if (action != null)
{
if (!key.Equals("WOL"))
{
ExecuteAction(action, parameters);
}
continue;
}
if (ExternalServiceHandler != null && parameters != null)
{
if (ExternalServiceHandler(key, parameters))
{
continue;
}
}
if (keySpec.Length == 2)
{
SendKey(mouse, key, int.Parse(keySpec[1]));
}
else
{
SendKey(mouse, key);
}
}
}
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, int delay = 180)
{
if (key.Length == 1 && int.TryParse(key, out _))
{
key = "_" + key;
}
var button = (ButtonType)Enum.Parse(typeof(ButtonType), key);
mouse.SendButton(button);
Thread.Sleep(delay);
}
public async Task<LgWebOsMouseService> GetMouseAsync()
{
return await _lgTvApi.GetMouse();
}
public async Task<IEnumerable<LgApp>> GetApps(bool force = false)
{
if (!await Connected(force))
{
Logger.Debug("Cannot refresh apps: no connection could be made");
return new List<LgApp>();
}
return await _lgTvApi.GetApps(force);
}
internal async Task<bool> PowerOff()
{
if (!await Connected(true) || CurrentState != PowerState.Active)
{
return false;
}
PoweredOffViaApp = true;
_poweredOffViaAppDateTime = DateTimeOffset.Now;
await _lgTvApi.TurnOff();
return true;
}
internal async Task<bool> TestConnection(int retries = 1)
{
if (!await Connected(true, retries))
{
return false;
}
try
{
await _lgTvApi.IsMuted();
return true;
}
catch (Exception ex)
{
Logger.Error("TestConnection: " + ex.ToLogString());
return false;
}
}
internal async Task<bool> WakeAndConnect(int wakeDelay = 5000, int connectDelay = 500)
{
try
{
//if (wakeDelay == 0)
//{
// if (await ConnectToSelectedDevice())
// {
// Logger.Debug("Already connected, no wake needed?");
// return;
// };
//}
await Task.Delay(wakeDelay);
var result = Wake();
if (!result)
{
Logger.Debug("WOL failed");
return false;
}
Logger.Debug("WOL succeeded");
await Task.Delay(connectDelay);
result = await Connect();
Logger.Debug("Connect succeeded: " + result);
return result;
}
catch (Exception e)
{
Logger.Error("WakeAndConnectToSelectedDevice: " + e.ToLogString());
return false;
}
}
internal bool Wake()
{
var result = false;
if (MacAddress != null)
{
result = WOL.WakeFunction(MacAddress);
_justWokeUp = true;
}
else
{
Logger.Debug("Cannot wake device: the device has no MAC-address");
}
return result;
}
internal async Task<bool> WakeAndConnectWithRetries(int retries = 5)
{
var wakeDelay = 0;
var maxRetries = retries <= 1 ? 5 : retries;
var result = false;
for (var retry = 0; retry < maxRetries && !result; retry++)
{
Logger.Debug($"WakeAndConnectWithRetries: attempt {retry + 1} of {maxRetries}...");
var ms = DateTimeOffset.Now.ToUnixTimeMilliseconds();
result = await WakeAndConnect(retry == 0 ? wakeDelay : 0);
ms = DateTimeOffset.Now.ToUnixTimeMilliseconds() - ms;
if (!result)
{
var delay = 2000 - ms;
if (delay > 0)
{
await Task.Delay((int)delay);
}
}
}
if (result)
{
var resumeScript = Path.Combine(Program.DataDir, "ResumeScript.bat");
if (File.Exists(resumeScript))
{
Utils.StartProcess(resumeScript);
}
}
return result;
}
public List<InvokableAction> GetInvokableActions()
{
return _invokableActions;
}
public async Task PowerOn()
{
var mouse = await _lgTvApi.GetMouse();
mouse.SendButton(ButtonType.POWER);
}
public void Test()
{
try
{
//_lgTvApi.GetServiceList();
//_lgTvApi?.Test();
}
catch (Exception ex)
{
Logger.Error("TEST: " + ex.ToLogString());
}
}
private async Task<bool> Connected(bool reconnect = false, int retries = 3)
{
if (reconnect || !IsConnected() || !string.Equals(_lgTvApi.GetIpAddress(), IpAddress))
{
if (!await Connect(retries))
{
Logger.Debug("Cannot apply LG-preset: no connection could be made");
return false;
}
}
return true;
}
internal void ConvertToCustom()
{
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());
}
public async Task SetOLEDMotionPro(string mode)
{
await _lgTvApi.SetConfig("tv.model.motionProMode", mode);
}
internal async Task SetConfig(string key, string value)
{
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 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;
}
}
}
+41 -5
View File
@@ -1,4 +1,5 @@
using LgTv;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -6,13 +7,20 @@ namespace ColorControl
{
class LgPreset : PresetBase
{
public string name { get; set; }
public static List<LgApp> LgApps { get; set; }
public static List<LgDevice> LgDevices { get; set; }
public string appId { get; set; }
public List<string> steps { get; set; }
public string DeviceMacAddress { get; set; }
public static List<LgApp> LgApps { get; set; }
[JsonIgnore]
public int X { get; set; }
[JsonIgnore]
public int Y { get; set; }
public LgPreset()
public LgPreset() : base()
{
steps = new List<string>();
}
@@ -38,14 +46,37 @@ namespace ColorControl
public static List<string> GetColumnNames()
{
return new List<string>() { "Name", "App|200", "Steps|400", "Shortcut" };
return new List<string>() { "Name", "Device|120", "App|200", "Steps|400", "Shortcut", "Trigger" };
}
public List<string> GetDisplayValues()
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>();
values.Add(name);
var deviceString = "Global";
if (!string.IsNullOrEmpty(DeviceMacAddress))
{
if (LgDevices != null)
{
var device = LgDevices.FirstOrDefault(d => !string.IsNullOrEmpty(d.MacAddress) && d.MacAddress.Equals(DeviceMacAddress));
if (device != null)
{
deviceString = device.Name;
}
else
{
deviceString = "Unknown: device not found";
}
}
else
{
deviceString = DeviceMacAddress;
}
}
values.Add(deviceString);
var app = appId;
if (LgApps != null)
{
@@ -59,6 +90,11 @@ namespace ColorControl
values.Add(GetStepsDisplay());
values.Add(shortcut);
if (Triggers.Any())
{
values.Add(Triggers.First().ToString());
}
return values;
}
+693 -215
View File
File diff suppressed because it is too large. Load diff
+13 -7
View File
@@ -1,25 +1,31 @@
namespace ColorControl
using System.Collections.Generic;
namespace ColorControl
{
class LgServiceConfig
{
public bool PowerOnAfterStartup { get; set; }
public bool PowerOnAfterResume { get; set; }
public bool PowerOffOnShutdown { get; set; }
public bool PowerOffOnStandby { get; set; }
public int PowerOnDelayAfterResume { get; set; }
public int PowerOnRetries { get; set; }
public string PreferredMacAddress { get; set; }
public string DeviceSearchKey { get; set; }
public bool UseOldNpcapWol { get; set; }
public List<LgDevice> Devices { get; set; }
public bool ShowRemoteControl { get; set; }
public LgServiceConfig()
{
PowerOnDelayAfterResume = 5000;
DeviceSearchKey = "[LG]";
PowerOnRetries = 10;
Devices = new List<LgDevice>();
}
}
}
+1375 -405
View File
File diff suppressed because it is too large. Load diff
+1569 -662
View File
File diff suppressed because it is too large. Load diff
+6
View File
@@ -120,6 +120,12 @@
<metadata name="mnuNvPresets.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="mnuAmdPresets.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>278, 17</value>
</metadata>
<metadata name="mnuLgExpert.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>583, 17</value>
</metadata>
<metadata name="mnuLgButtons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>147, 17</value>
</metadata>
+246
View File
@@ -0,0 +1,246 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
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)
{
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
public static void ErrorOk(string text)
{
MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
public static void InfoOk(string text, string title = null, string url = null)
{
MessageBox.Show(text, title ?? MainForm?.Text, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0, url);
}
public static DialogResult QuestionYesNo(string text)
{
return MessageBox.Show(text, MainForm?.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
}
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<string> labels, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
{
var fieldDefinitions = labels.Select(l => new FieldDefinition
{
Label = l
}).ToList();
return ShowDialog(caption, fieldDefinitions, validateFunc);
}
public static List<FieldDefinition> ShowDialog(string caption, IEnumerable<FieldDefinition> fields, Func<IEnumerable<FieldDefinition>, string> validateFunc = null)
{
var values = new List<FieldDefinition>();
var prompt = new Form()
{
Width = 500,
Height = 90 + (fields.Count() * 55),
FormBorderStyle = FormBorderStyle.FixedDialog,
Text = caption,
StartPosition = FormStartPosition.CenterScreen
};
var top = 10;
var boxes = new List<Control>();
foreach (var field in fields)
{
var label = field.Label;
var textLabel = new Label() { Left = 40, Top = top, Text = label, AutoSize = true };
top += 25;
prompt.Controls.Add(textLabel);
Control control;
switch(field.FieldType)
{
case FieldType.Text:
var textBox = new MaskedTextBox() { Left = 40, Top = top, Width = 400 };
if (label.Contains("Ip-address"))
{
textBox.Mask = "###.###.###.###";
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(control);
boxes.Add(control);
control.Tag = field;
}
var confirmation = new Button() { Text = "OK", Left = 365, Width = 75, Top = prompt.ClientRectangle.Height - 30 };
confirmation.Click += (sender, e) =>
{
foreach (var box in boxes)
{
var field = (FieldDefinition)box.Tag;
switch (field.FieldType)
{
case FieldType.Flags:
var checkedListBox = (CheckedListBox)box;
var compoundValue = 0;
var enumValue = 1;
for (var i = 0; i < checkedListBox.Items.Count; i++)
{
compoundValue += checkedListBox.GetItemChecked(i) ? enumValue : 0;
enumValue *= 2;
}
field.Value = compoundValue;
break;
default:
field.Value = box.Text.Trim();
break;
}
}
if (validateFunc != null)
{
var message = validateFunc(fields);
if (!string.IsNullOrEmpty(message))
{
WarningOk(message);
return;
}
}
values.AddRange(fields);
prompt.Close();
};
prompt.Controls.Add(confirmation);
prompt.AcceptButton = confirmation;
prompt.ShowDialog(MainForm);
return values;
}
public static Form ShowProgress(string caption)
{
var prompt = new Form()
{
Width = 300,
Height = 100,
FormBorderStyle = FormBorderStyle.FixedToolWindow,
Text = caption,
StartPosition = FormStartPosition.CenterScreen,
ControlBox = false
};
var textLabel = new Label() { Left = 20, Top = 25, Text = caption, AutoSize = true };
prompt.Controls.Add(textLabel);
prompt.Show(MainForm);
return prompt;
}
}
}
+21
View File
@@ -0,0 +1,21 @@
using NvAPIWrapper.Display;
using System.Collections.Generic;
namespace ColorControl
{
class NvDisplayInfo
{
public Display Display { get; }
public List<string> Values { get; }
public string InfoLine { get; }
public NvDisplayInfo(Display display, List<string> values, string infoLine)
{
Display = display;
Values = values;
InfoLine = infoLine;
}
}
}
+65 -28
View File
@@ -19,8 +19,10 @@ namespace ColorControl
public uint refreshRate { get; set; }
public bool primaryDisplay { get; set; }
public string displayName { get; set; }
public uint ditheringBits { get; set; }
public uint ditheringMode { get; set; }
public NvPreset()
public NvPreset() : base()
{
applyColorData = true;
applyDithering = false;
@@ -31,6 +33,8 @@ namespace ColorControl
applyRefreshRate = false;
refreshRate = 60;
primaryDisplay = true;
ditheringBits = 1;
ditheringMode = 4;
}
public NvPreset(ColorData colorData) : this()
@@ -47,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;
@@ -69,13 +73,15 @@ namespace ColorControl
public static string[] GetColumnNames()
{
//return new[] { "BPC", "Format", "Dynamic range", "Toggle HDR", "Shortcut" };
return new[] { "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut" };
return new[] { "Name", "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Dithering", "HDR", "Shortcut", "Apply on startup" };
}
public List<string> GetDisplayValues()
public override List<string> GetDisplayValues(Config config = null)
{
var values = new List<string>();
values.Add(name);
var display = string.Format("{0}", primaryDisplay ? "Primary" : displayName);
values.Add(display);
@@ -83,7 +89,10 @@ namespace ColorControl
values.Add(colorSettings);
values.Add(string.Format("{0}: {1}Hz", applyRefreshRate ? "Included" : "Excluded", refreshRate));
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", ditheringEnabled ? "Enabled" : "Disabled"));
var dithering = GetDitheringDescription();
values.Add(string.Format("{0}: {1}", applyDithering ? "Included" : "Excluded", dithering));
values.Add(string.Format("{0}: {1}", applyHDR ? "Included" : "Excluded", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled"));
//values.Add(colorData.ColorDepth.ToString());
@@ -92,9 +101,23 @@ namespace ColorControl
//values.Add(toggleHDR.ToString());
values.Add(shortcut);
values.Add(string.Format("{0}", config?.NvPresetId_ApplyOnStartup == id ? "Yes" : string.Empty));
return values;
}
public string GetDitheringDescription(string disabledText = "Disabled")
{
var dithering = ditheringEnabled ? string.Empty : disabledText;
if (ditheringEnabled)
{
var ditherBitsDescription = ((NvDitherBits)ditheringBits).GetDescription();
var ditherModeDescription = ((NvDitherMode)ditheringMode).GetDescription();
dithering = string.Format("{0} {1}", ditherBitsDescription, ditherModeDescription);
}
return dithering;
}
public static List<NvPreset> GetDefaultPresets()
{
var presets = new List<NvPreset>();
@@ -121,30 +144,39 @@ namespace ColorControl
public string GetTextForMenuItem()
{
var sb = new StringBuilder();
if (displayName != null)
if (!string.IsNullOrEmpty(name))
{
sb.AppendFormat("Display: {0} / ", displayName);
}
if (applyColorData)
{
var colorSettings = string.Format("Format: {0}, {1}, {2}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange);
sb.Append(colorSettings);
sb.Append(name);
sb.Append(" / ");
}
if (applyRefreshRate)
else
{
sb.AppendFormat("{0}Hz", refreshRate);
sb.Append(" / ");
}
if (applyDithering)
{
sb.AppendFormat("Dithering: {0}", ditheringEnabled ? "Yes" : "No");
sb.Append(" / ");
}
if (applyHDR)
{
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
sb.Append(" / ");
if (displayName != null)
{
sb.AppendFormat("Display: {0} / ", displayName);
}
if (applyColorData)
{
var colorSettings = string.Format("Format: {0}, {1}, {2}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange);
sb.Append(colorSettings);
sb.Append(" / ");
}
if (applyRefreshRate)
{
sb.AppendFormat("{0}Hz", refreshRate);
sb.Append(" / ");
}
if (applyDithering)
{
var dithering = GetDitheringDescription("No");
sb.AppendFormat("Dithering: {0}", dithering);
sb.Append(" / ");
}
if (applyHDR)
{
sb.AppendFormat("HDR: {0}", toggleHDR ? "Toggle" : HDREnabled ? "Enabled" : "Disabled");
sb.Append(" / ");
}
}
return sb.ToString(0, Math.Max(0, sb.Length - 3));
@@ -174,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);
}
}
+317 -32
View File
@@ -1,16 +1,46 @@
using NvAPIWrapper;
using NvAPIWrapper.Display;
using NvAPIWrapper.Native.Display;
using NvAPIWrapper.Native.Display.Structures;
using NvAPIWrapper.Native.GPU.Structures;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ColorControl
{
class NvService : GraphicsService
enum NvDitherBits
{
[Description("6-bit")]
Bits6 = 0,
[Description("8-bit")]
Bits8 = 1,
[Description("10-bit")]
Bits10 = 2
}
enum NvDitherMode
{
[Description("Spatial Dynamic")]
SpatialDynamic = 0,
[Description("Spatial Static")]
SpatialStatic = 1,
[Description("Spatial Dynamic 2x2")]
SpatialDynamic2x2 = 2,
[Description("Spatial Static 2x2")]
SpatialStatic2x2 = 3,
[Description("Temporal")]
Temporal = 4
}
class NvService : GraphicsService<NvPreset>
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
[DllImport(@"nvapi64", EntryPoint = @"nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl,
PreserveSig = true)]
private static extern IntPtr NvAPI64_QueryInterface(uint interfaceId);
@@ -26,9 +56,9 @@ namespace ColorControl
public delegate long NvAPI_Disp_GetDitherControl(
[In] PhysicalGPUHandle physicalGpu,
[In] uint OutputId,
[In][Out] IntPtr ditherControl);
[In] IntPtr ditherControl);
[StructLayout(LayoutKind.Sequential, Pack = 8)]
[StructLayout(LayoutKind.Sequential)]
public struct NV_GPU_DITHER_CONTROL_V1
{
public int version;
@@ -38,10 +68,35 @@ namespace ColorControl
};
private Display _currentDisplay;
private bool _initialized = false;
public NvService()
public NvService(string dataPath) : base(dataPath, "NvPresets.json")
{
var converter = new ColorDataConverter();
_JsonDeserializer.RegisterConverters(new[] { converter });
LoadPresets();
}
public static bool ExecutePresetAsync(string idOrName)
{
try
{
var nvService = new NvService(Program.DataDir);
var result = nvService.ApplyPreset(idOrName);
if (!result)
{
Console.WriteLine("Preset not found or error while executing.");
}
return result;
}
catch (Exception ex)
{
Console.WriteLine("Error executing preset: " + ex.ToLogString());
return false;
}
}
public Display GetCurrentDisplay()
@@ -54,6 +109,41 @@ namespace ColorControl
return _currentDisplay;
}
public override bool HasDisplaysAttached()
{
try
{
return DisplayDevice.GetGDIPrimaryDisplayDevice() != null;
}
catch (Exception)
{
return false;
}
}
public void GlobalSave()
{
SavePresets();
}
protected override List<NvPreset> GetDefaultPresets()
{
return NvPreset.GetDefaultPresets();
}
private void SavePresets()
{
try
{
var json = _JsonSerializer.Serialize(_presets);
File.WriteAllText(_presetsFilename, json);
}
catch (Exception e)
{
Logger.Error(e.ToLogString());
}
}
private void SetCurrentDisplay(NvPreset preset)
{
if (preset.primaryDisplay)
@@ -68,51 +158,157 @@ namespace ColorControl
}
public bool ApplyPreset(NvPreset preset, Config config)
public bool ApplyPreset(string idOrName)
{
var preset = GetPresetByIdOrName(idOrName);
if (preset != null)
{
return ApplyPreset(preset, Program.AppContext);
}
else
{
return false;
}
}
public bool ApplyPreset(NvPreset preset, AppContext appContext)
{
var result = true;
if (!HasDisplaysAttached())
{
return false;
}
SetCurrentDisplay(preset);
if (preset.applyHDR)
{
if (preset.toggleHDR || preset.HDREnabled != IsHDREnabled())
{
ToggleHDR(config.DisplaySettingsDelay);
}
}
var hdrEnabled = IsHDREnabled();
if (preset.applyRefreshRate)
{
SetRefreshRate(preset.refreshRate);
}
var newHdrEnabled = preset.applyHDR && (preset.HDREnabled || (preset.toggleHDR && !hdrEnabled));
var applyHdr = preset.applyHDR && (preset.toggleHDR || preset.HDREnabled != hdrEnabled);
if (preset.applyColorData)
if (preset.applyColorData && (ColorDataDiffers(preset.colorData) || (!newHdrEnabled && preset.applyColorData && preset.colorData.Colorimetry != ColorDataColorimetry.Auto)))
{
var display = GetCurrentDisplay();
//var data = new ColorData(ColorDataFormat.YUV444, ColorDataColorimetry.Auto, ColorDataDynamicRange.Auto, ColorDataDepth.BPC10, preset.colorData.SelectionPolicy, ColorDataDesktopDepth.Default);
if (hdrEnabled)
{
SetHDRState(display, false);
applyHdr = false;
}
try
{
display.DisplayDevice.SetColorData(preset.colorData);
}
catch (Exception ex)
{
Logger.Error($"SetColorData threw an exception: {ex.Message}");
result = false;
}
//var master = display.DisplayDevice.HDRColorData.MasteringDisplayData;
//var newMaster = new MasteringDisplayColorData(master.FirstColorCoordinate, master.SecondColorCoordinate, master.ThirdColorCoordinate, new ColorDataColorCoordinate(0.25f, 0.25f), 50, 0, 50, 50);
//var hdr = new HDRColorData(ColorDataHDRMode.UHDA, newMaster, ColorDataFormat.YUV444, ColorDataDynamicRange.Auto, ColorDataDepth.BPC8);
//display.DisplayDevice.SetHDRColorData(hdr);
if (hdrEnabled && newHdrEnabled)
{
SetHDRState(display, true, useSwitch: appContext.StartUpParams.NoGui);
applyHdr = false;
}
}
if (applyHdr)
{
var display = GetCurrentDisplay();
var colorData = preset.applyColorData ? preset.colorData : display.DisplayDevice.CurrentColorData;
SetHDRState(display, newHdrEnabled, colorData, appContext.StartUpParams.NoGui);
}
if (preset.applyRefreshRate)
{
if (!SetRefreshRate(preset.refreshRate))
{
result = false;
}
}
if (preset.applyDithering)
{
SetDithering(preset.ditheringEnabled);
if (!SetDithering(preset.ditheringEnabled, preset: preset))
{
result = false;
}
}
return true;
_lastAppliedPreset = preset;
PresetApplied();
return result;
}
public void SetDithering(bool enabled)
private ColorData GetCurrentColorData(Display display)
{
try
{
return display.DisplayDevice.CurrentColorData;
}
catch (Exception e)
{
Logger.Error("Error while reading current color data: " + e.Message);
return new ColorData();
}
}
private bool ColorDataDiffers(ColorData colorData)
{
var display = GetCurrentDisplay();
var currentColorData = GetCurrentColorData(display);
var settingRange = colorData.DynamicRange == ColorDataDynamicRange.Auto ? colorData.ColorFormat == ColorDataFormat.RGB ? ColorDataDynamicRange.VESA : ColorDataDynamicRange.CEA : colorData.DynamicRange;
var settingSpace = colorData.Colorimetry;
return colorData.ColorFormat != currentColorData.ColorFormat || colorData.ColorDepth != currentColorData.ColorDepth || settingRange != currentColorData.DynamicRange || settingSpace != currentColorData.Colorimetry;
}
private void SetHDRState(Display display, bool enabled, ColorData colorData = null, bool useSwitch = false)
{
if (useSwitch)
{
if (enabled != IsHDREnabled())
{
CCD.SetHDRState(enabled, display.Name);
//ToggleHDR();
}
return;
}
CCD.SetHDRState(enabled, display.Name);
//var newMaster = new MasteringDisplayColorData();
//var hdr = new HDRColorData(enabled ? ColorDataHDRMode.UHDA : ColorDataHDRMode.Off, newMaster, colorData?.ColorFormat, colorData?.DynamicRange, colorData?.ColorDepth);
//display.DisplayDevice.SetHDRColorData(hdr);
// HDR will not always be disabled this way, then we can only disable it through the display settings
//if (!enabled && IsHDREnabled())
//{
// CCD.SetHDRState(enabled);
// //ToggleHDR();
//}
//else if (enabled)
//{
// // Currectly there seems to be a bug that after enabling HDR via NVAPI, some settings are only applied upon opening the Display Settings...
// ////OpenDisplaySettings();
//}
}
public bool SetDithering(bool enabled, uint bits = 1, uint mode = 4, NvPreset preset = null)
{
var result = true;
var ptr = NvAPI64_QueryInterface(0xDF0DFCDD);
if (ptr != IntPtr.Zero)
{
@@ -123,16 +319,34 @@ namespace ColorControl
var gpuHandle = displayDevice.PhysicalGPU.Handle;
var displayId = displayDevice.DisplayId;
var result = delegateValue(gpuHandle, displayId, (uint)(enabled ? 1 : 2), 1, 4);
if (result != 0)
if (preset != null)
{
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {result}");
bits = preset.ditheringBits;
mode = preset.ditheringMode;
}
var resultValue = delegateValue(gpuHandle, displayId, (uint)(enabled ? 1 : 2), bits, mode);
if (resultValue != 0)
{
Logger.Error($"Could not set dithering because NvAPI_Disp_SetDitherControl returned a non-zero return code: {resultValue}");
result = false;
}
}
else
{
Logger.Error($"Could not set dithering because the function NvAPI_Disp_SetDitherControl could not be found");
result = false;
}
return result;
}
public bool GetDithering()
{
// Requires elevation, too bad :(
//Utils.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\nvlddmkm\State\DisplayDatabase", "DitherRegistryKey");
/*
var ptr = NvAPI64_QueryInterface(0x932AC8FB);
if (ptr != IntPtr.Zero)
{
@@ -145,7 +359,8 @@ namespace ColorControl
NV_GPU_DITHER_CONTROL_V1 info = new NV_GPU_DITHER_CONTROL_V1();
info.version = 1;
IntPtr bla = Marshal.AllocHGlobal(Marshal.SizeOf(info.GetType()));
var size = Marshal.SizeOf(info.GetType());
IntPtr bla = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(info, bla, false);
// Does not work yet...What is the exact interface of NvAPI_Disp_GetDitherControl?
@@ -158,16 +373,22 @@ namespace ColorControl
return info.state == 1;
}
*/
return false;
}
public bool SetRefreshRate(uint refreshRate)
{
var display = GetCurrentDisplay();
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
var timing = display.DisplayDevice.CurrentTiming;
if (timing.Extra.RefreshRate == refreshRate)
{
return true;
}
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
return SetRefreshRateInternal(display.Name, refreshRate, portrait, timing.HorizontalVisible, timing.VerticalVisible);
}
@@ -197,6 +418,70 @@ namespace ColorControl
return Display.GetDisplays();
}
public List<NvDisplayInfo> GetDisplayInfos()
{
Display[] displays;
try
{
displays = GetDisplays();
}
catch (Exception e)
{
Logger.Error("Error while getting displays: " + e.ToLogString());
return null;
}
var list = new List<NvDisplayInfo>();
foreach (var display in displays)
{
var values = new List<string>();
values.Add("Current settings");
var name = display.Name;
var screen = Screen.AllScreens.FirstOrDefault(x => x.DeviceName.Equals(name));
if (screen != null)
{
name += " (" + screen.DeviceFriendlyName() + ")";
}
values.Add(name);
var colorData = GetCurrentColorData(display);
var colorSettings = string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry);
values.Add(colorSettings);
var refreshRate = display.DisplayDevice.CurrentTiming.Extra.RefreshRate;
values.Add($"{refreshRate}Hz");
var lastPreset = GetLastAppliedPreset();
if (lastPreset != null)
{
values.Add(lastPreset.GetDitheringDescription());
}
else
{
values.Add(string.Empty);
}
var hdrEnabled = IsHDREnabled();
values.Add(hdrEnabled ? "Yes" : "No");
var infoLine = string.Format("{0}: {1}, {2}Hz, HDR: {3}", name, colorSettings, refreshRate, hdrEnabled ? "Yes" : "No");
var displayInfo = new NvDisplayInfo(display, values, infoLine);
list.Add(displayInfo);
}
return list;
}
protected override void Initialize()
{
NVIDIA.Initialize();
+17 -5
View File
@@ -1,11 +1,14 @@
using Windows.Devices.Enumeration;
using Newtonsoft.Json;
using Windows.Devices.Enumeration;
using Windows.Devices.Enumeration.Pnp;
namespace ColorControl
{
class PnpDev
{
[JsonIgnore]
public DeviceInformation DevInfo { get; private set; }
[JsonIgnore]
public PnpObject PnpObject { get; private set; }
public string Name { get; private set; }
@@ -13,18 +16,27 @@ namespace ColorControl
public string MacAddress { get; private set; }
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress)
[JsonConstructor]
public PnpDev(string name, string ipAddress, string macAddress)
{
DevInfo = devInfo;
PnpObject = pnpObject;
Name = name;
IpAddress = ipAddress;
MacAddress = macAddress;
}
public PnpDev(DeviceInformation devInfo, PnpObject pnpObject, string name, string ipAddress, string macAddress) : this(name, ipAddress, macAddress)
{
DevInfo = devInfo;
PnpObject = pnpObject;
}
public override string ToString()
{
return Name + " (" + IpAddress + ")";
return (IsCustom() ? "Custom: " : "Auto detect: ") + $"{Name}" + (!string.IsNullOrEmpty(IpAddress) ? $"({IpAddress})" : string.Empty);
}
public bool IsCustom()
{
return DevInfo == null;
}
}
}
+144
View File
@@ -1,14 +1,123 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace ColorControl
{
enum PresetTriggerType
{
[Description("None")]
None = 0,
[Description("Startup")]
Startup = 1,
[Description("Resume")]
Resume = 2,
[Description("Shutdown")]
Shutdown = 3,
[Description("Standby")]
Standby = 4,
[Description("Screensaver")]
Screensaver = 5,
[Description("Process switch")]
ProcessSwitch = 6,
}
[Flags]
enum PresetConditionType
{
[Description("None")]
None = 0,
[Description("SDR")]
SDR = 1,
[Description("HDR")]
HDR = 2,
[Description("Full Screen")]
FullScreen = 4,
[Description("Notifications Disabled")]
NotificationsDisabled = 8,
}
class PresetTrigger
{
public PresetTriggerType Trigger { get; set; }
public List<string> IncludedProcesses { get; set; }
public List<string> ExcludedProcesses { get; set; }
public PresetConditionType Conditions { get; set; }
public PresetTrigger()
{
IncludedProcesses = new List<string>();
ExcludedProcesses = new List<string>();
}
public bool TriggerActive(PresetTriggerContext context)
{
var active = Conditions == PresetConditionType.None || (Conditions.HasFlag(PresetConditionType.SDR) && !context.IsHDRActive) || (Conditions.HasFlag(PresetConditionType.HDR) && context.IsHDRActive);
var allProcesses = IncludedProcesses.Contains("*");
if (Trigger == PresetTriggerType.ProcessSwitch)
{
active = active && (allProcesses || (context.ChangedProcesses?.Any() ?? false));
if (active)
{
var included = allProcesses || context.ChangedProcesses.Any(cp => IncludedProcesses.Any(ip => cp.ProcessName.Equals(ip, StringComparison.OrdinalIgnoreCase)));
var excluded = context.ChangedProcesses.Any(cp => ExcludedProcesses.Any(ep => cp.ProcessName.Equals(ep, StringComparison.OrdinalIgnoreCase)));
var screenSizeCheck = (!Conditions.HasFlag(PresetConditionType.FullScreen) && !context.ForegroundProcessIsFullScreen) ||
(context.ForegroundProcess != null && context.ForegroundProcessIsFullScreen &&
(allProcesses || IncludedProcesses.Any(ip => context.ForegroundProcess.ProcessName.Equals(ip, StringComparison.OrdinalIgnoreCase))));
var notificationsDisabledCheck = !Conditions.HasFlag(PresetConditionType.NotificationsDisabled) || context.IsNotificationDisabled;
active = included && !excluded && screenSizeCheck && notificationsDisabledCheck;
}
}
return active;
}
public override string ToString()
{
if (Trigger == PresetTriggerType.None)
{
return string.Empty;
}
return $"On {Trigger.GetDescription()} of {DisplayProcesses(IncludedProcesses)}{(ExcludedProcesses.Any() ? ", excluding " + DisplayProcesses(ExcludedProcesses) : string.Empty)}{(Conditions > 0 ? ", only in " + string.Join(", ", Utils.GetDescriptions<PresetConditionType>((int)Conditions)) : string.Empty)}";
}
public static string DisplayProcesses(IEnumerable<string> processes)
{
return processes.Contains("*") ? "all processes" : string.Join(", ", processes);
}
}
class PresetTriggerContext
{
public bool IsHDRActive { get; set; }
public Process ForegroundProcess { get; set; }
public bool ForegroundProcessIsFullScreen { get; set; }
public List<Process> ChangedProcesses { get; set; }
public bool IsNotificationDisabled { get; set; }
}
internal abstract class PresetBase
{
public string name { get; set; }
public string shortcut { get; set; }
public List<PresetTrigger> Triggers { get; set; }
private int _id;
protected PresetBase()
{
Triggers = new List<PresetTrigger>();
}
public int id
{
get
@@ -45,5 +154,40 @@ namespace ColorControl
return id;
}
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();
}
}
}
+162 -7
View File
@@ -1,4 +1,8 @@
using System;
using NLog;
using NWin32;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
@@ -6,6 +10,11 @@ namespace ColorControl
{
static class Program
{
public static string DataDir { get; private set; }
public static string ConfigFilename { get; private set; }
public static Config Config { get; private set; }
public static AppContext AppContext { get; private set; }
/// <summary>
/// The main entry point for the application.
/// </summary>
@@ -18,21 +27,43 @@ namespace ColorControl
// Handler for exceptions in threads behind forms.
Application.ThreadException += GlobalThreadExceptionHandler;
DataDir = Utils.GetDataPath();
InitLogger();
LoadConfig();
var startUpParams = StartUpParams.Parse(args);
if (startUpParams.ActivateChromeFontFix || startUpParams.DeactivateChromeFontFix)
AppContext = new AppContext(Config, startUpParams);
var existingProcess = Utils.GetProcessByName("ColorControl");
try
{
Utils.InstallChromeFix(startUpParams.ActivateChromeFontFix);
return;
if (HandleStartupParams(startUpParams, existingProcess))
{
return;
}
}
finally
{
Utils.CloseConsole();
}
string mutexId = $"Global\\{typeof(MainForm).GUID}";
bool mutexCreated;
var mutex = new Mutex(true, mutexId, out mutexCreated);
var mutex = new Mutex(true, mutexId, out var mutexCreated);
try
{
if (!mutexCreated)
{
if (existingProcess != null && existingProcess.Threads.Count > 0)
{
var thread = existingProcess.Threads[0];
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
return;
}
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
}
else
@@ -41,7 +72,7 @@ namespace ColorControl
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(startUpParams));
Application.Run(new MainForm(AppContext));
}
catch (Exception ex)
{
@@ -58,6 +89,130 @@ namespace ColorControl
}
}
private static void InitLogger()
{
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: File and Console
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = Path.Combine(DataDir, "LogFile.txt") };
//var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
//config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
config.AddRule(LogLevel.Trace, LogLevel.Fatal, logfile);
// Apply config
LogManager.Configuration = config;
}
private static void LoadConfig()
{
ConfigFilename = Path.Combine(DataDir, "Settings.json");
if (File.Exists(ConfigFilename))
{
var data = File.ReadAllText(ConfigFilename);
Config = Utils.JsonSerializer.Deserialize<Config>(data);
}
else
{
Config = new Config();
}
}
private static bool HandleStartupParams(StartUpParams startUpParams, Process existingProcess)
{
if (startUpParams.ActivateChromeFontFix || startUpParams.DeactivateChromeFontFix)
{
Utils.InstallChromeFix(startUpParams.ActivateChromeFontFix);
return true;
}
var useConsole = startUpParams.NoGui || existingProcess != null;
if (!useConsole)
{
return false;
}
startUpParams.NoGui = true;
var result = false;
if (startUpParams.ExecuteHelp)
{
Utils.OpenConsole();
Console.WriteLine("\nColorControl CLI");
Console.WriteLine("Syntax : ColorControl command options");
Console.WriteLine("Commands:");
Console.WriteLine("--nvpreset <preset name or id>: execute NVIDIA-preset");
Console.WriteLine("--amdpreset <preset name or id>: execute AMD-preset");
Console.WriteLine("--lgpreset <preset name> : execute LG-preset");
Console.WriteLine("--help : displays this help info");
Console.WriteLine("Options:");
Console.WriteLine("--nogui: starts command from the command line and will not open GUI (is forced when GUI is already running)");
result = true;
}
if (startUpParams.ExecuteLgPreset)
{
Utils.OpenConsole();
Console.WriteLine($"Executing LG-preset '{startUpParams.LgPresetName}'...");
var task = LgService.ExecutePresetAsync(startUpParams.LgPresetName);
var taskResult = Utils.WaitForTask(task);
if (taskResult)
{
Console.WriteLine("Done.");
}
result = true;
}
if (startUpParams.ExecuteNvidiaPreset)
{
Utils.OpenConsole();
Console.WriteLine($"Executing NVIDIA-preset '{startUpParams.NvidiaPresetIdOrName}'...");
var taskResult = NvService.ExecutePresetAsync(startUpParams.NvidiaPresetIdOrName);
if (taskResult)
{
Console.WriteLine("Done.");
}
result = true;
}
if (startUpParams.ExecuteAmdPreset)
{
Utils.OpenConsole();
Console.WriteLine($"Executing AMD-preset '{startUpParams.AmdPresetIdOrName}'...");
var taskResult = AmdService.ExecutePresetAsync(startUpParams.AmdPresetIdOrName);
if (taskResult)
{
Console.WriteLine("Done.");
}
result = true;
}
return result;
}
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
{
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
return 1;
}
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
+3 -3
View File
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Maassoft")]
[assembly: AssemblyProduct("ColorControl")]
[assembly: AssemblyCopyright("Copyright © Maassoft 2020")]
[assembly: AssemblyCopyright("Copyright © Maassoft 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -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("1.3.6.0")]
[assembly: AssemblyFileVersion("1.3.6.0")]
[assembly: AssemblyVersion("4.2.3.0")]
[assembly: AssemblyFileVersion("4.2.3.0")]
+55 -26
View File
@@ -8,10 +8,10 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace ColorControl.Properties
{
namespace ColorControl.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@@ -19,53 +19,82 @@ namespace ColorControl.Properties
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorControl.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set
{
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap LG_remote {
get {
object obj = ResourceManager.GetObject("LG_remote", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized string similar to Power;120;132
///1;106;228
///2;196;228
///3;286;228
///4;106;296
///5;196;296
///6;286;296
///7;106;362
///8;196;362
///9;286;362
///0;196;425
///Vol +;98;513
///Mute;198;513
///Channel +;298;513
///Vol -;98;622
///Speech;198;633
///Channel -;298;622
///Home;78;695
///Settings;318;695.
/// </summary>
internal static string RC_coords {
get {
return ResourceManager.GetString("RC_coords", resourceCulture);
}
}
}
}
+15 -5
View File
@@ -46,7 +46,7 @@
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
@@ -85,9 +87,10 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
@@ -109,9 +112,16 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="LG_remote" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\LG_remote.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="RC_coords" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\RC_coords.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root>
+197
View File
@@ -0,0 +1,197 @@
namespace ColorControl
{
partial class RemoteControlForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RemoteControlForm));
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.btnClose = new System.Windows.Forms.Button();
this.lblUC = new System.Windows.Forms.Label();
this.chkDirectMode = new System.Windows.Forms.CheckBox();
this.pnlMouse = new System.Windows.Forms.Panel();
this.lblPos = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Location = new System.Drawing.Point(12, 185);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(817, 120);
this.flowPanel.TabIndex = 0;
this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
this.flowPanel.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.flowPanel_PreviewKeyDown);
//
// btnClose
//
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.FlatAppearance.BorderColor = System.Drawing.Color.DarkGray;
this.btnClose.FlatAppearance.BorderSize = 0;
this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnClose.ForeColor = System.Drawing.Color.White;
this.btnClose.Location = new System.Drawing.Point(826, 0);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(14, 23);
this.btnClose.TabIndex = 1;
this.btnClose.Text = "x";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// lblUC
//
this.lblUC.AutoSize = true;
this.lblUC.ForeColor = System.Drawing.Color.Maroon;
this.lblUC.Location = new System.Drawing.Point(12, 0);
this.lblUC.Name = "lblUC";
this.lblUC.Size = new System.Drawing.Size(100, 13);
this.lblUC.TabIndex = 0;
this.lblUC.Text = "Under construction!";
//
// chkDirectMode
//
this.chkDirectMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.chkDirectMode.AutoSize = true;
this.chkDirectMode.ForeColor = System.Drawing.Color.White;
this.chkDirectMode.Location = new System.Drawing.Point(12, 718);
this.chkDirectMode.Name = "chkDirectMode";
this.chkDirectMode.Size = new System.Drawing.Size(105, 17);
this.chkDirectMode.TabIndex = 2;
this.chkDirectMode.Text = "Direct Mode (F1)";
this.chkDirectMode.UseVisualStyleBackColor = true;
this.chkDirectMode.CheckedChanged += new System.EventHandler(this.chkDirectMode_CheckedChanged);
//
// pnlMouse
//
this.pnlMouse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pnlMouse.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pnlMouse.Location = new System.Drawing.Point(12, 311);
this.pnlMouse.Name = "pnlMouse";
this.pnlMouse.Size = new System.Drawing.Size(817, 401);
this.pnlMouse.TabIndex = 3;
this.pnlMouse.MouseEnter += new System.EventHandler(this.pnlMouse_MouseEnter);
this.pnlMouse.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pnlMouse_MouseMove);
this.pnlMouse.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pnlMouse_MouseUp);
//
// lblPos
//
this.lblPos.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.lblPos.AutoSize = true;
this.lblPos.ForeColor = System.Drawing.Color.Maroon;
this.lblPos.Location = new System.Drawing.Point(749, 718);
this.lblPos.Name = "lblPos";
this.lblPos.Size = new System.Drawing.Size(0, 13);
this.lblPos.TabIndex = 1;
//
// label1
//
this.label1.AutoSize = true;
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(12, 26);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(538, 52);
this.label1.TabIndex = 4;
this.label1.Text = resources.GetString("label1.Text");
//
// label2
//
this.label2.AutoSize = true;
this.label2.ForeColor = System.Drawing.Color.White;
this.label2.Location = new System.Drawing.Point(231, 91);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(216, 52);
this.label2.TabIndex = 5;
this.label2.Text = "Mouse:\r\n- left button: activate/open item\r\n- right button: go back\r\n- wheel: scro" +
"ll through menus (only vertically)";
//
// label3
//
this.label3.AutoSize = true;
this.label3.ForeColor = System.Drawing.Color.White;
this.label3.Location = new System.Drawing.Point(12, 91);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(180, 91);
this.label3.TabIndex = 6;
this.label3.Text = "Keyboard keys:\r\n- arrow keys: navigate the menu\r\n- backspace: go back\r\n- escape: " +
"exit\r\n- S: open picture settings\r\n- H: open Home\r\n- media keys: volume up/down/m" +
"ute";
//
// RemoteControlForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(841, 735);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.lblPos);
this.Controls.Add(this.pnlMouse);
this.Controls.Add(this.chkDirectMode);
this.Controls.Add(this.lblUC);
this.Controls.Add(this.btnClose);
this.Controls.Add(this.flowPanel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.KeyPreview = true;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "RemoteControlForm";
this.Opacity = 0.8D;
this.Text = "RemoteControlForm";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.RemoteControlForm_FormClosed);
this.Load += new System.EventHandler(this.RemoteControlForm_Load);
this.Shown += new System.EventHandler(this.RemoteControlForm_Shown);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyDown);
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.RemoteControlForm_KeyUp);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
this.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.RemoteControlForm_PreviewKeyDown);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Label lblUC;
private System.Windows.Forms.CheckBox chkDirectMode;
private System.Windows.Forms.Panel pnlMouse;
private System.Windows.Forms.Label lblPos;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
}
}
+242
View File
@@ -0,0 +1,242 @@
using LgTv;
using NWin32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ColorControl
{
partial class RemoteControlForm : Form
{
private LgService _lgService;
private List<LgPreset> _buttons;
private LgWebOsMouseService _mouseService;
private int _maxX, _maxY;
private int _posX, _posY;
public RemoteControlForm(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
InitializeComponent();
pnlMouse.MouseWheel += new MouseEventHandler(pnlMouse_MouseWheel);
}
private void RemoteControlForm_Load(object sender, EventArgs e)
{
foreach (var preset in _buttons)
{
var button = new Button();
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderColor = Color.DarkGray;
button.FlatAppearance.MouseOverBackColor = Color.DarkGray;
button.Text = preset.name;
button.Click += ButtonClick;
button.Tag = preset;
button.Parent = flowPanel;
}
}
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
var _ = _lgService.ApplyPreset(preset);
}
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void RemoteControlForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
var intPtr = new UIntPtr(2);
NativeMethods.SendMessageW(Handle, NativeConstants.WM_NCLBUTTONDOWN, intPtr, IntPtr.Zero);
}
}
private void RemoteControlForm_KeyDown(object sender, KeyEventArgs e)
{
//var key = e.KeyCode;
//HandleKey(key);
}
private void flowPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void HandleKey(Keys key)
{
if (!chkDirectMode.Checked)
{
return;
}
var cvt = new KeysConverter();
var shortcut = (string)cvt.ConvertTo(key, typeof(string));
var preset = _buttons.FirstOrDefault(p => p.shortcut != null && p.shortcut.Equals(shortcut));
if (preset != null)
{
var _ = _lgService.ApplyPreset(preset);
}
}
private void RemoteControlForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
//HandleKey(e.KeyCode);
}
private void RemoteControlForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
chkDirectMode.Checked = !chkDirectMode.Checked;
return;
}
HandleKey(e.KeyCode);
}
private void chkDirectMode_CheckedChanged(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
// MessageForms.InfoOk(
//@"If you enable Direct Mode you will be able to control the TV directly via your keyboard and mouse.
//Note that your mouse will be captured in a box and that due to pointer accelaration the control will not be perfect.
//Keyboard keys:
//- arrow keys: navigate the menu
//- backspace: go back
//- escape: exit
//- S: open picture settings
//- media keys: volume up/down/mute
//Mouse:
//- left button: activate/open item
//- right button: go back
//- wheel: scroll through menus (only vertically)
//Press F1 to stop Direct Mode."
// );
if (_mouseService == null)
{
_lgService.GetMouseAsync().ContinueWith((task) => BeginInvoke(new Action<Task<LgWebOsMouseService>>(SetMouseService), new[] { task }));
}
}
else
{
Cursor.Clip = Rectangle.Empty;
}
}
private void SetMouseService(Task<LgWebOsMouseService> mouseServiceTask)
{
_mouseService = mouseServiceTask.Result;
_maxX = 1920;
_maxY = 1080;
_posX = _maxX / 2;
_posY = _maxY / 2;
}
private void flowPanel_MouseMove(object sender, MouseEventArgs e)
{
}
private void pnlMouse_MouseEnter(object sender, EventArgs e)
{
if (chkDirectMode.Checked)
{
Cursor.Clip = RectangleToScreen(pnlMouse.Bounds);
}
}
private void RemoteControlForm_FormClosed(object sender, FormClosedEventArgs e)
{
Cursor.Clip = Rectangle.Empty;
}
private void pnlMouse_MouseUp(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
if (e.Button == MouseButtons.Left)
{
_mouseService.Click();
}
else
{
HandleKey(Keys.Back);
}
}
}
private void HandleMouse(MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var scaleX = (double)e.X / pnlMouse.Width;
var scaleY = (double)e.Y / pnlMouse.Height;
var relX = scaleX * _maxX;
var relY = scaleY * _maxY;
var dx = (int)relX - _posX;
var dy = (int)relY - _posY;
_posX = Math.Min(_posX + dx, _maxX);
_posY = Math.Min(_posY + dy, _maxY);
lblPos.Text = $"Pos: {_posX}, {_posY}";
//var cx = flowPanel.Width / 2;
//var cy = flowPanel.Height / 2;
//var dx = (e.X - cx) / 10;
//var dy = (e.Y - cy) / 10;
_mouseService.Move(dx, dy);
}
}
private void RemoteControlForm_Shown(object sender, EventArgs e)
{
chkDirectMode.Checked = true;
}
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
{
HandleMouse(e);
}
private void pnlMouse_MouseWheel(object sender, MouseEventArgs e)
{
if (chkDirectMode.Checked && _mouseService != null)
{
var dy = e.Delta * SystemInformation.MouseWheelScrollLines / 120;
if (dy != 0)
{
_mouseService.Scroll(0, dy);
}
}
}
}
}
+126
View File
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="label1.Text" xml:space="preserve">
<value>This form allows you to control the TV directly via your keyboard and mouse.
Note that your mouse will be captured in a box and that due to pointer acceleration the control will not be perfect.
Press F1 to stop Direct Mode.</value>
</data>
</root>
+81
View File
@@ -0,0 +1,81 @@
namespace ColorControl
{
partial class RemoteControlPanel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.pbRemote = new System.Windows.Forms.PictureBox();
this.btnRcDirect = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).BeginInit();
this.SuspendLayout();
//
// pbRemote
//
this.pbRemote.Dock = System.Windows.Forms.DockStyle.Fill;
this.pbRemote.Image = global::ColorControl.Properties.Resources.LG_remote;
this.pbRemote.Location = new System.Drawing.Point(0, 0);
this.pbRemote.Name = "pbRemote";
this.pbRemote.Size = new System.Drawing.Size(129, 468);
this.pbRemote.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pbRemote.TabIndex = 0;
this.pbRemote.TabStop = false;
this.pbRemote.Click += new System.EventHandler(this.pbRemote_Click);
this.pbRemote.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbRemote_MouseMove);
//
// btnRcDirect
//
this.btnRcDirect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnRcDirect.Location = new System.Drawing.Point(71, 7);
this.btnRcDirect.Name = "btnRcDirect";
this.btnRcDirect.Size = new System.Drawing.Size(55, 23);
this.btnRcDirect.TabIndex = 3;
this.btnRcDirect.Text = "Direct";
this.btnRcDirect.UseVisualStyleBackColor = true;
this.btnRcDirect.Click += new System.EventHandler(this.button1_Click);
//
// RemoteControlPanel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.Controls.Add(this.btnRcDirect);
this.Controls.Add(this.pbRemote);
this.Name = "RemoteControlPanel";
this.Size = new System.Drawing.Size(129, 468);
this.Load += new System.EventHandler(this.RemoteControlPanel_Load);
((System.ComponentModel.ISupportInitialize)(this.pbRemote)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox pbRemote;
private System.Windows.Forms.Button btnRcDirect;
}
}
+174
View File
@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace ColorControl
{
partial class RemoteControlPanel : UserControl
{
private static int Range = 35;
private LgService _lgService;
private List<LgPreset> _buttons;
private RemoteControlForm _remoteControlForm;
private Bitmap _backupImage;
private LgPreset _lastHoveredPreset;
public RemoteControlPanel(LgService lgService, List<LgPreset> buttons)
{
_lgService = lgService;
_buttons = buttons;
InitializeComponent();
_backupImage = new Bitmap(pbRemote.Image);
var txtCoords = Utils.GetResourceFile("RC_coords.txt");
foreach (var line in txtCoords.Split('\n'))
{
var columns = line.Split(';');
var name = columns[0];
var preset = _buttons.FirstOrDefault(p => p.name.Equals(name));
if (preset != null)
{
preset.X = int.Parse(columns[1]);
preset.Y = int.Parse(columns[2]);
}
}
}
private void RemoteControlPanel_Load(object sender, EventArgs e)
{
}
private void ButtonClick(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var preset = (LgPreset)(sender as Button).Tag;
var _ = _lgService.ApplyPreset(preset);
}
}
private void pbRemote_Click(object sender, EventArgs e)
{
if (e is MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
if (preset != null)
{
var _ = _lgService.ApplyPreset(preset);
}
}
}
private (int, int) GetCoordsInImage(int curX, int curY)
{
var width = pbRemote.Width;
var aspect = 0.27D;
var currentAspect = (double)pbRemote.Width / pbRemote.Height;
if (currentAspect > aspect)
{
width = (int)(aspect * pbRemote.Height);
curX = curX - ((pbRemote.Width - width) / 2);
}
else
{
var height = (int)(pbRemote.Width / aspect);
curY = curY - ((pbRemote.Height - height) / 2);
}
var factor = 405D / width;
var posX = (int)(curX * factor);
var posY = (int)(curY * factor);
return (posX, posY);
}
private void btnRcDirect_Click(object sender, EventArgs e)
{
}
private void pbRemote_MouseMove(object sender, MouseEventArgs mouseArgs)
{
var (posX, posY) = GetCoordsInImage(mouseArgs.X, mouseArgs.Y);
var preset = _buttons.FirstOrDefault(p => posX >= (p.X - Range) && posX <= (p.X + Range) && posY >= (p.Y - Range) && posY <= (p.Y + Range));
var refresh = false;
if (_lastHoveredPreset != null && (preset == null || preset != _lastHoveredPreset))
{
var image = (Bitmap)pbRemote.Image;
for (var y = Math.Max(0, _lastHoveredPreset.Y - Range); y < _lastHoveredPreset.Y + Range; y++)
{
for (var x = Math.Max(0, _lastHoveredPreset.X - Range); x < _lastHoveredPreset.X + Range; x++)
{
image.SetPixel(x, y, _backupImage.GetPixel(x, y));
}
}
refresh = true;
}
if (preset != null && preset != _lastHoveredPreset)
{
var image = (Bitmap)pbRemote.Image;
var rec = new Rectangle(Math.Max(0, preset.X - Range), Math.Max(0, preset.Y - Range), Range * 2, Range * 2);
using (var g = Graphics.FromImage(image))
{
using (var cloud_brush = new SolidBrush(Color.FromArgb(128, Color.White)))
{
g.FillEllipse(cloud_brush, rec);
}
}
//for (var y = preset.Y - 30; y < preset.Y + 30; y++)
//{
// for (var x = preset.X - 30; x < preset.X + 30; x++)
// {
// var pixel = image.GetPixel(x, y);
// image.SetPixel(x, y, Color.FromArgb(150, 0, 0));
// }
//}
refresh = true;
}
if (refresh)
{
pbRemote.Refresh();
}
_lastHoveredPreset = preset;
}
private void button1_Click(object sender, EventArgs e)
{
if (_remoteControlForm == null || _remoteControlForm.IsDisposed)
{
var buttons = _lgService.GetRemoteControlButtons();
_remoteControlForm = new RemoteControlForm(_lgService, buttons);
_remoteControlForm.Show();
}
else
{
_remoteControlForm.Show();
}
}
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

+37
View File
@@ -0,0 +1,37 @@
Power;120;136
1;106;228
2;196;228
3;286;228
4;106;296
5;196;296
6;286;296
7;106;362
8;196;362
9;286;362
List;106;425
0;196;425
SAP;286;425
Vol +;98;513
Mute;198;513
Channel +;298;513
Vol -;98;622
Channel -;298;622
Home;78;695
Settings;318;695
Up;200;748
Left;98;852
Enter;200;852
Right;304;852
Down;200;952
Back;80;1000
TV Guide;320;1000
Netflix;92;1076
Inputs;200;1066
Amazon Prime;312;1076
Red;90;1150
Green;164;1150
Yellow;240;1150
Blue;316;1150
Rakuten TV;116;1218
Play;202;1218
Pause;291;1218
+2 -2
View File
@@ -32,7 +32,7 @@ namespace ColorControl
// Activate the subscription
watcher.Enabled = true;
}
catch (EventLogReadingException e)
catch (EventLogReadingException)
{
}
}
@@ -81,7 +81,7 @@ namespace ColorControl
// }
//}
}
catch (Exception e)
catch (Exception)
{
}
}
+138
View File
@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Script.Serialization;
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, string presetsBaseFilename)
{
_dataPath = dataPath;
_presetsBaseFilename = presetsBaseFilename;
_JsonSerializer = new JavaScriptSerializer();
_JsonDeserializer = new JavaScriptSerializer();
Initialize();
}
~ServiceBase()
{
Uninitialize();
}
public List<T> GetPresets()
{
return _presets;
}
public T GetPresetByIdOrName(string idOrName)
{
var preset = _presets.FirstOrDefault(p => p.name != null && p.name.Equals(idOrName, StringComparison.OrdinalIgnoreCase));
if (preset == null && int.TryParse(idOrName, out var id))
{
preset = _presets.FirstOrDefault(p => p.id == id);
}
return preset;
}
public T GetLastAppliedPreset()
{
return _lastAppliedPreset;
}
protected virtual void Initialize()
{
}
protected virtual void Uninitialize()
{
}
protected void PresetApplied()
{
AfterApplyPreset?.Invoke(this, _lastAppliedPreset);
}
protected void LoadPresets()
{
_presetsFilename = Path.Combine(_dataPath, _presetsBaseFilename);
_presetsBackupFilename = Path.Combine(_dataPath, $"{_presetsBaseFilename}.backup");
try
{
var presetsExists = File.Exists(_presetsFilename);
if (presetsExists)
{
try
{
var json = File.ReadAllText(_presetsFilename);
// Hack to convert incorrect triggers
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
_presets = _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();
}
}
+68
View File
@@ -7,16 +7,55 @@ namespace ColorControl
public const string RunningFromScheduledTaskParam = "--scheduled";
public const string ActivateChromeFontFixParam = "--activate-chrome-font-fix";
public const string DeactivateChromeFontFixParam = "--deactivate-chrome-font-fix";
public const string ExecuteHelpParam = "--help";
public const string ExecuteNvidiaPresetParam = "--nvpreset";
public const string ExecuteAmdPresetParam = "--amdpreset";
public const string ExecuteLgPresetParam = "--lgpreset";
public const string NoGuiParam = "--nogui";
public bool RunningFromScheduledTask { get; private set; }
public bool ActivateChromeFontFix { get; private set; }
public bool DeactivateChromeFontFix { get; private set; }
public bool ExecuteHelp { get; private set; }
public bool ExecuteNvidiaPreset { get; private set; }
public string NvidiaPresetIdOrName { get; private set; }
public bool ExecuteAmdPreset { get; private set; }
public string AmdPresetIdOrName { get; private set; }
public bool ExecuteLgPreset { get; private set; }
public string LgPresetName { get; private set; }
public bool NoGui { get; set; }
public static StartUpParams Parse(IEnumerable<string> args)
{
var settings = new StartUpParams();
var parseNameParam = string.Empty;
foreach (var arg in args)
{
if (!string.IsNullOrEmpty(parseNameParam))
{
switch (parseNameParam)
{
case ExecuteNvidiaPresetParam:
{
settings.NvidiaPresetIdOrName = arg;
break;
}
case ExecuteAmdPresetParam:
{
settings.AmdPresetIdOrName = arg;
break;
}
case ExecuteLgPresetParam:
{
settings.LgPresetName = arg;
break;
}
}
parseNameParam = string.Empty;
continue;
}
switch (arg.ToLowerInvariant())
{
case RunningFromScheduledTaskParam:
@@ -34,6 +73,35 @@ namespace ColorControl
settings.DeactivateChromeFontFix = true;
break;
}
case ExecuteHelpParam:
{
settings.ExecuteHelp = true;
settings.NoGui = true;
break;
}
case ExecuteNvidiaPresetParam:
{
settings.ExecuteNvidiaPreset = true;
parseNameParam = ExecuteNvidiaPresetParam;
break;
}
case ExecuteAmdPresetParam:
{
settings.ExecuteAmdPreset = true;
parseNameParam = ExecuteAmdPresetParam;
break;
}
case ExecuteLgPresetParam:
{
settings.ExecuteLgPreset = true;
parseNameParam = ExecuteLgPresetParam;
break;
}
case NoGuiParam:
{
settings.NoGui = true;
break;
}
}
}
return settings;
+41
View File
@@ -0,0 +1,41 @@
using Microsoft.Win32;
namespace ColorControl
{
static class UserSessionInfo
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public static bool UserLocalSession { get; private set; } = true;
public static SessionSwitchReason LastSessionSwitchReason { get; private set; } = 0;
public delegate void UserSessionSwitchHandler(bool toLocal);
public static event UserSessionSwitchHandler UserSessionSwitch;
public static void Install()
{
SystemEvents.SessionSwitch += SessionSwitchHandler;
}
public static void SessionSwitchHandler(object sender, SessionSwitchEventArgs evt)
{
if (evt.Reason == SessionSwitchReason.ConsoleDisconnect)
{
Logger.Debug("Detected a disconnect from the console");
UserLocalSession = false;
UserSessionSwitch(false);
}
else if (evt.Reason == SessionSwitchReason.ConsoleConnect)
{
Logger.Debug("Detected a connect to the console");
if (!UserLocalSession)
{
Logger.Debug("Session state switched to local");
UserLocalSession = true;
UserSessionSwitch(true);
}
}
LastSessionSwitchReason = evt.Reason;
}
}
}
+769 -14
View File
@@ -1,20 +1,32 @@
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
using Newtonsoft.Json.Linq;
using NStandard;
using NWin32;
using NWin32.NativeTypes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Deployment.Application;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using Windows.Devices.Enumeration;
using Windows.Devices.Enumeration.Pnp;
namespace ColorControl
{
class Utils
static class Utils
{
[Flags]
public enum ModKeys : int
@@ -25,15 +37,82 @@ namespace ColorControl
Win = 8
}
public static string PKEY_PNPX_IpAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12297";
public static string PKEY_PNPX_PhysicalAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12294";
public 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;
public const int ENUM_REGISTRY_SETTINGS = -2;
public static string PKEY_PNPX_IpAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12297";
public static string PKEY_PNPX_PhysicalAddress = "{656a3bb3-ecc0-43fd-8477-4ae0404a96cd} 12294";
public delegate void PCREATE_PROCESS_NOTIFY_ROUTINE(IntPtr ParentId, IntPtr ProcessId, bool Create);
public static JavaScriptSerializer JsonSerializer = new JavaScriptSerializer();
public static bool ConsoleOpened { get; private set; }
[DllImport("user32.dll", SetLastError = true)]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason);
[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettingsA(string deviceName, int modeNum, out DEVMODEA devMode);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool AttachConsole(int processId);
[DllImport("kernel32.dll", SetLastError = true)]
[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);
//[DllImport("Wtsapi32.dll")]
//public extern static bool WTSUnRegisterSessionNotification(IntPtr hWnd);
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)
{
Bitmap bitmap2 = (Bitmap) bitmap.Clone();
Bitmap bitmap2 = (Bitmap)bitmap.Clone();
int shift = -1;
bool shiftRed = true;
@@ -188,6 +267,21 @@ namespace ColorControl
return key != null;
}
internal static void GetRegistryKeyValue(string keyname, string valueName, bool deepSearch = false)
{
var key = Registry.LocalMachine.OpenSubKey(keyname);
foreach (var subKeyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(subKeyName);
if (subKey.GetValueNames().Contains(valueName))
{
Thread.Sleep(0);
}
}
}
internal static bool UpdateShortcut(string path, string arguments, bool removeArguments = false)
{
if (File.Exists(path))
@@ -219,10 +313,10 @@ namespace ColorControl
object objValue;
string value = null;
foreach (var key in device.Properties.Keys)
{
Debug.WriteLine(key);
}
//foreach (var key in device.Properties.Keys)
//{
// Debug.WriteLine(key);
//}
if (device.Properties.TryGetValue(propertyName, out objValue))
{
@@ -253,9 +347,11 @@ namespace ColorControl
var list = await DeviceInformation.FindAllAsync("", new List<string>());
var reqs = new List<string>();
reqs.Add(PKEY_PNPX_IpAddress);
reqs.Add(PKEY_PNPX_PhysicalAddress);
var reqs = new List<string>
{
PKEY_PNPX_IpAddress,
PKEY_PNPX_PhysicalAddress
};
foreach (var dev in list)
{
@@ -284,11 +380,21 @@ namespace ColorControl
return devices;
}
public static void WaitForTask(Task task)
public static T WaitForTask<T>(Task<T> task)
{
while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete))
{
Thread.Sleep(100);
Thread.Sleep(10);
Application.DoEvents();
}
return task.Result;
}
public static void WaitForTask(System.Threading.Tasks.Task task)
{
while (task != null && (task.Status < TaskStatus.WaitingForChildrenToComplete))
{
Thread.Sleep(10);
Application.DoEvents();
}
}
@@ -302,5 +408,654 @@ namespace ColorControl
if ((bool)t.GetField("added", hidden).GetValue(ni))
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
}
internal static Image GenerateGradientBitmap(int width, int height)
{
var bitmap = new Bitmap(512, height);
for (var i = 0; i < 256; i++)
{
var pixel = Color.FromArgb(i, i, i);
for (var h = 0; h < height; h++)
{
bitmap.SetPixel(i * 2, h, pixel);
bitmap.SetPixel(i * 2 + 1, h, pixel);
}
}
return bitmap;
}
public static string GetDescription<T>(this T e) where T : IConvertible
{
if (e is Enum)
{
var type = e.GetType();
var values = Enum.GetValues(type);
foreach (int val in values)
{
if (val == e.ToInt32(CultureInfo.InvariantCulture))
{
var memInfo = type.GetMember(type.GetEnumName(val));
var descriptionAttribute = memInfo[0]
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute;
if (descriptionAttribute != null)
{
return descriptionAttribute.Description;
}
}
}
}
return null; // could also return string.Empty
}
public static List<string> GetDescriptions<T>(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;
}
public static void RegisterTask(string taskName, bool enabled)
{
var file = Assembly.GetExecutingAssembly().Location;
var directory = Path.GetDirectoryName(file);
try
{
using (TaskService ts = new TaskService())
{
if (enabled)
{
TaskDefinition td = ts.NewTask();
td.RegistrationInfo.Description = "Start ColorControl";
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name });
td.Actions.Add(new ExecAction(file, StartUpParams.RunningFromScheduledTaskParam, directory));
ts.RootFolder.RegisterTaskDefinition(taskName, td);
}
else
{
ts.RootFolder.DeleteTask(taskName, false);
}
}
}
catch (Exception e)
{
MessageForms.ErrorOk("Could not create/delete task: " + e.Message);
}
}
public static bool TaskExists(string taskName, bool update)
{
var file = Assembly.GetExecutingAssembly().Location;
using (TaskService ts = new TaskService())
{
var task = ts.RootFolder.Tasks.FirstOrDefault(x => x.Name.Equals(taskName));
if (task != null)
{
if (update && ApplicationDeployment.IsNetworkDeployed)
{
var action = task.Definition.Actions.FirstOrDefault(x => x.ActionType == TaskActionType.Execute);
if (action != null)
{
var execAction = action as ExecAction;
if (!execAction.Path.Equals(file))
{
RegisterTask(taskName, false);
RegisterTask(taskName, true);
}
}
}
return true;
}
return false;
}
}
public static bool RegisterShortcut(IntPtr handle, int id, string shortcut, bool clear = false)
{
if (clear)
{
UnregisterHotKey(handle, id);
}
if (!string.IsNullOrEmpty(shortcut))
{
var (mods, key) = ParseShortcut(shortcut);
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(ToolStripDropDownItem mnuParent, string name, Type enumType, object colorData, string propertyName, EventHandler clickEvent)
{
PropertyInfo property = null;
var subMenuItems = mnuParent.DropDownItems.Find("miColorSettings_" + name, false);
ToolStripMenuItem subMenuItem;
if (subMenuItems.Length == 0)
{
subMenuItem = (ToolStripMenuItem)mnuParent.DropDownItems.Add(name);
subMenuItem.Name = "miColorSettings_" + name;
if (colorData != null)
{
property = colorData.GetType().GetDeclaredProperty(propertyName);
subMenuItem.Tag = property;
}
foreach (var enumValue in Enum.GetValues(enumType))
{
var item = subMenuItem.DropDownItems.Add(enumValue.ToString());
item.Tag = enumValue;
item.Click += clickEvent;
}
}
else
{
subMenuItem = (ToolStripMenuItem)subMenuItems[0];
property = (PropertyInfo)subMenuItem.Tag;
}
if (colorData == null)
{
return;
}
var value = property.GetValue(colorData);
foreach (var item in subMenuItem.DropDownItems)
{
if (item is ToolStripMenuItem)
{
var menuItem = (ToolStripMenuItem)item;
if (menuItem.Tag != null)
{
menuItem.Checked = menuItem.Tag.Equals(value);
}
}
}
}
public static void BuildComboBox<T>(ComboBox comboBox, params T[] skip) where T : IConvertible
{
if (comboBox.Items.Count == 0)
{
foreach (var enumValue in Enum.GetValues(typeof(T)))
{
if (skip.Contains((T)enumValue))
{
continue;
}
var item = new EnumComboBoxItem
{
Name = ((T)enumValue).GetDescription(),
Id = (int)enumValue
};
comboBox.Items.Add(item);
}
}
}
public static int SetComboBoxEnumIndex(ComboBox comboBox, int value)
{
if (comboBox.Items.Count == 0)
{
return -1;
}
var index = 0;
for (var i = 0; i < comboBox.Items.Count; i++)
{
var item = comboBox.Items[i] as EnumComboBoxItem;
if (item.Id == value)
{
index = i;
break;
}
}
comboBox.SelectedIndex = index;
return index;
}
public static T GetComboBoxEnumItem<T>(ComboBox comboBox) where T : IConvertible
{
if (comboBox.SelectedIndex == -1)
{
return (T)Enum.Parse(typeof(T), "None");
}
var item = comboBox.SelectedItem as EnumComboBoxItem;
return (T)Enum.Parse(typeof(T), item.Id.ToString());
}
public static string FormatKeyboardShortcut(KeyEventArgs keyEvent)
{
var pressedModifiers = keyEvent.Modifiers;
//Debug.WriteLine("KD: " + e.Modifiers + ", " + e.KeyCode);
var shortcutString = (pressedModifiers > 0 ? pressedModifiers.ToString() : "");
if (keyEvent.KeyCode == Keys.LWin || WinKeyDown)
{
WinKeyDown = true;
if (!string.IsNullOrEmpty(shortcutString))
{
shortcutString += ", ";
}
shortcutString += "Win";
}
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)
{
if (!empty)
{
shortcutString += " + ";
}
shortcutString += keyEvent.KeyCode.ToString();
}
if (string.IsNullOrEmpty(shortcutString))
{
keyEvent.SuppressKeyPress = true;
}
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)
{
WinKeyDown = false;
}
}
public static void InitListView(ListView listView, IEnumerable<string> columns)
{
foreach (var name in columns)
{
var columnName = name;
var parts = name.Split('|');
var width = 120;
if (parts.Length > 1)
{
width = int.Parse(parts[1]);
columnName = parts[0];
}
var header = listView.Columns.Add(columnName);
header.Width = width == 120 ? -2 : width;
}
}
public static void AddOrUpdateListItem<T>(ListView listView, List<T> presets, Config config, T preset = null, ListViewItem specItem = null) where T : PresetBase
{
ListViewItem item = null;
if (preset == null)
{
item = listView.SelectedItems[0];
preset = (T)item.Tag;
}
else
{
item = specItem;
}
if (preset.id == 0)
{
preset.id = preset.GetHashCode();
}
var values = preset.GetDisplayValues(config);
if (item == null)
{
item = listView.Items.Add(values[0]);
item.Tag = preset;
for (var i = 1; i < values.Count; i++)
{
item.SubItems.Add(values[i]);
}
if (!presets.Any(x => x.id == preset.id))
{
presets.Add(preset);
}
}
else
{
item.Text = values[0];
for (var i = 1; i < values.Count; i++)
{
if (item.SubItems.Count == i)
{
item.SubItems.Add(values[i]);
}
else
{
item.SubItems[i].Text = values[i];
}
}
}
}
public static bool OpenConsole()
{
if (ConsoleOpened)
{
return true;
}
if (!AttachConsole(-1))
{
AllocConsole();
return true;
}
ConsoleOpened = true;
return false;
}
public static bool CloseConsole()
{
if (!ConsoleOpened)
{
return false;
}
SendKeys.SendWait("{ENTER}");
var result = FreeConsole();
return result;
}
public static Process GetProcessByName(string name, bool skipCurrent = true)
{
var currentProcessId = Process.GetCurrentProcess().Id;
foreach (var process in Process.GetProcesses())
{
if (process.ProcessName.Equals(name) && (!skipCurrent || process.Id != currentProcessId))
{
return process;
}
}
return null;
}
public static void StartProcess(string fileName, string arguments = null)
{
var process = Process.Start(fileName, arguments);
}
public static string GetResourceFile(string resourceName)
{
resourceName = "ColorControl.Resources." + resourceName;
var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
var result = reader.ReadToEnd();
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
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
}
+120 -7
View File
@@ -1,22 +1,126 @@
using NLog;
using System;
using System.Globalization;
using PacketDotNet;
using System.Net.NetworkInformation;
using SharpPcap;
using System.Diagnostics;
using SharpPcap.Npcap;
using System.Linq;
using System.Net;
using System.Net.Sockets;
namespace ColorControl
{
class WOL
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length.";
private const string ArgumentExceptionInvalidPasswordLength = "Invalid password length.";
private const int DefaultWolPort = 9;
public static bool WakeFunction(string macAddress, bool usePcap = false)
{
if (usePcap)
{
return WakeFunctionPcap(macAddress);
}
return WakeFunctionToAllNics(macAddress);
}
public static bool WakeFunctionToAllNics(string macAddress)
{
var result = false;
try
{
var address = PhysicalAddress.Parse(macAddress);
var addressBytes = address.GetAddressBytes();
var data = GetWolPacket(addressBytes);
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (var ni in interfaces)
{
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.Name.Contains("VirtualBox"))
{
continue;
}
Logger.Debug($"Found network: {ni.Name} ({ni.Description}), type: {ni.NetworkInterfaceType}");
try
{
if (/*ni.OperationalStatus == OperationalStatus.Up &&*/ ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() != null)
{
foreach (var uip in ni.GetIPProperties().UnicastAddresses)
{
if (uip.Address.ToString().StartsWith("169.254"))
{
continue;
}
if (uip.Address.AddressFamily == AddressFamily.InterNetwork)
{
Logger.Debug($"Broadcast WOL in network: {ni.Name} ({ni.Description}), local address: {uip.Address}");
var local = new IPEndPoint(uip.Address, 0);
var udpc = new UdpClient(local);
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
var target = new IPEndPoint(IPAddress.Broadcast, DefaultWolPort);
udpc.Send(data, data.Length, target);
result = true;
}
}
}
}
catch (Exception ex)
{
Logger.Error($"WakeFunctionToAllNics: while sending to specific network: {ni.Name} ({ni.Description}): {ex.ToLogString()}");
}
}
}
catch (Exception ex)
{
Logger.Error($"WakeFunctionToAllNics: {ex.ToLogString()}");
}
return result;
}
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="password"/> is not 0 or 6.</exception>
private static byte[] GetWolPacket(byte[] macAddress, byte[] password = null)
{
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
if (macAddress.Length != 6)
throw new ArgumentException(ArgumentExceptionInvalidMacAddressLength);
password = password ?? new byte[0];
if (password.Length != 0 && password.Length != 6)
throw new ArgumentException(ArgumentExceptionInvalidPasswordLength);
var packet = new byte[17 * 6 + password.Length];
int offset, i;
for (offset = 0; offset < 6; ++offset)
packet[offset] = 0xFF;
for (offset = 6; offset < 17 * 6; offset += 6)
for (i = 0; i < 6; ++i)
packet[i + offset] = macAddress[i];
if (password.Length > 0)
{
for (offset = 16 * 6 + 6; offset < (17 * 6 + password.Length); offset += 6)
for (i = 0; i < 6; ++i)
packet[i + offset] = password[i];
}
return packet;
}
//You need SharpPcap for this to work
public static void WakeFunction(string macAddress)
private static bool WakeFunctionPcap(string macAddress)
{
/* Retrieve the device list */
var devices = CaptureDeviceList.Instance;
@@ -27,7 +131,7 @@ namespace ColorControl
if (devices.Count < 1)
{
Logger.Debug("No network device found on this machine");
return;
return false;
}
foreach (var device in devices)
@@ -40,7 +144,14 @@ namespace ColorControl
// }
//}
Logger.Debug("Opening network device: " + device.Name);
if (device is NpcapDevice npcapDevice)
{
Logger.Debug($"Opening network device (NpcapDevice): FriendlyName: {npcapDevice.Interface.FriendlyName}, Description: {npcapDevice.Interface.Description}");
}
else
{
Logger.Debug($"Opening network device (NOT A NpcapDevice!): {device.Name}, Description: {device.Description}");
}
//Open the device
device.Open();
@@ -68,6 +179,8 @@ namespace ColorControl
device.Close();
}
return true;
}
}
}
+26
View File
@@ -6,10 +6,12 @@ namespace LgTv
public class ClientKeyStore
{
private string ip;
public ClientKeyStore(string ip)
{
this.ip = ip;
}
public string GetClientKey()
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
@@ -19,10 +21,34 @@ namespace LgTv
}
return null;
}
public void SaveClientKey(string key)
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_ClientKey.txt");
File.WriteAllText(filename, key);
}
public bool HasValidHandShake(string expectedHandShake)
{
var handShake = GetHandShake();
return handShake?.Equals(expectedHandShake) ?? false;
}
public string GetHandShake()
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
if (File.Exists(filename))
{
return File.ReadAllText(filename);
}
return null;
}
public void SaveHandShake(string data)
{
var filename = Path.Combine(Utils.GetDataPath(), ip + "_HandShake.txt");
File.WriteAllText(filename, data);
}
}
}
+231 -18
View File
@@ -1,13 +1,10 @@
using ColorControl;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage;
namespace LgTv
{
@@ -19,9 +16,89 @@ 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 class LgTvApi:IDisposable
{
public bool ConnectionClosed { get => _connection == null || _connection.ConnectionClosed; }
public bool ConnectionClosed { get => _connection?.ConnectionClosed ?? true; }
private readonly LgTvApiCore _connection;
private LgWebOsMouseService _mouseConnection;
@@ -32,17 +109,11 @@ namespace LgTv
private List<App> _appList;
private string _ip;
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private const string BEFORE_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\",\"READ_SETTINGS\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
private const string AFTER_PAIR_HAND_SHAKE = "{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"client-key\":\"CLIENTKEYGOESHERE\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\",\"READ_SETTINGS\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}";
public Task ControlButton(object oK)
{
// throw new NotImplementedException();
return null;
}
private string _currentPariKey;
private string _currentPairKey;
private string webSocketUri;
public static async Task<LgTvApi> CreateLgTvApi(string ip, int retries = 1)
@@ -78,10 +149,10 @@ namespace LgTv
public async Task MakeHandShake()
{
_currentPariKey = _keyStore.GetClientKey();
if (_currentPariKey != null)
_currentPairKey = _keyStore.GetClientKey();
if (_currentPairKey != null && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(BEFORE_PAIR_HAND_SHAKE))
{
var key = AFTER_PAIR_HAND_SHAKE.Replace("CLIENTKEYGOESHERE", _currentPariKey);
var key = AFTER_PAIR_HAND_SHAKE.Replace("CLIENTKEYGOESHERE", _currentPairKey);
var conn = await _connection.SendCommandAsync(key);
_keyStore.SaveClientKey((string)conn.clientKey);
return;
@@ -89,12 +160,13 @@ namespace LgTv
dynamic result = await _connection.SendCommandAsync(BEFORE_PAIR_HAND_SHAKE);
_keyStore.SaveClientKey(result.clientKey);
_keyStore.SaveHandShake(BEFORE_PAIR_HAND_SHAKE);
}
public async Task<bool> Connect()
{
var ctx = _connection.Connect(new Uri(webSocketUri));
var ctx = _connection.Connect(new Uri(webSocketUri));
return await ctx;
}
@@ -259,7 +331,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)
@@ -278,6 +350,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>();
@@ -297,6 +399,15 @@ namespace LgTv
return _appList.OrderBy(e => e.Title);
}
public async Task<IEnumerable<string>> GetServiceList()
{
var list = new List<string>();
var requestMessage = new RequestMessage("channels", "ssap://api/getServiceList");
var command = _connection.SendCommandAsync(requestMessage);
var res = await command;
return list;
}
public async Task<IEnumerable<LgApp>> GetApps(bool force = false)
{
var appList = new List<LgApp>();
@@ -359,6 +470,108 @@ namespace LgTv
return (string)response.sessionId;
}
public async Task SetSystemSettings(string key, object value, string category = "picture")
{
string jsonValue;
var valueType = value.GetType();
var intType = typeof(int);
if (valueType.IsArray && intType.IsAssignableFrom(valueType.GetElementType()))
{
var values = ((Array)value).Cast<int>();
jsonValue = "[" + string.Join(", ", values.Select(x => x.ToString()).ToArray()) + ']';
}
else
{
jsonValue = $"\"{value}\"";
}
var lunauri = "luna://com.webos.settingsservice/setSystemSettings";
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" } }";
var @params = JObject.Parse(json);
await ExecuteRequest(lunauri, @params);
}
public async Task SetConfig(string key, string value)
{
var lunauri = "luna://com.webos.service.config/setConfigs";
var @params = JObject.Parse(@"{ ""configs"": { """ + key + @""": """ + value + @""" } }");
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[]
{
new {
label = "",
onClick = lunauri,
@params = @params
}
};
var payload = new
{
message = "Applying...",
buttons = buttons,
onclose = new { uri = lunauri, @params = @params },
onfail = new { uri = lunauri, @params = @params }
};
var requestMessage = new RequestMessage("ssap://system.notifications/createAlert", payload);
var response = await _connection.SendCommandAsync(requestMessage);
var alertId = (string)response.alertId;
if (alertId != null)
{
var closeAlert = new
{
alertId = alertId
};
requestMessage = new RequestMessage("ssap://system.notifications/closeAlert", closeAlert);
await _connection.SendCommandAsync(requestMessage);
}
}
public async Task<dynamic> GetSystemInfo(params string[] keys)
{
var payload = new
{
keys = keys
};
var requestMessage = new RequestMessage("ssap://system/getSystemInfo", payload);
var command = _connection.SendCommandAsync(requestMessage);
var res = await command;
return res;
}
public async Task<dynamic> GetSystemSettings(string category, params string[] keys)
{
var payload = new
{
category = category,
keys = keys
};
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload);
var command = _connection.SendCommandAsync(requestMessage);
var res = await command;
return res;
}
public void Close()
{
+116 -24
View File
@@ -1,15 +1,13 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.Web;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using Windows.Foundation;
using ColorControl;
namespace LgTv
{
@@ -31,9 +29,9 @@ 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 void Log(RawRequestMessage message) { }
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public async Task<bool> Connect(Uri uri, bool ignoreReceiver = false)
{
@@ -47,12 +45,29 @@ namespace LgTv
_connection.MessageReceived += Connection_MessageReceived;
_connection.Closed += Connection_Closed;
await _connection.ConnectAsync(uri);
using (var cancellationTokenSource = new CancellationTokenSource(5000))
{
var connectTask = _connection.ConnectAsync(uri).AsTask(cancellationTokenSource.Token);
var result = await connectTask.ContinueWith((antecedent) =>
{
if (antecedent.Status == TaskStatus.RanToCompletion)
{
// connectTask ran to completion, so we know that the MessageWebSocket is connected.
// Add additional code here to use the MessageWebSocket.
IsConnected?.Invoke(true);
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;
return result;
}
}
catch (Exception e)
{
@@ -67,41 +82,53 @@ namespace LgTv
}
return false;
}
}
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)
{
Logger.Error("SendMessageAsync: " + e.Message);
ConnectionClosed = true;
_messageWriter?.Dispose();
}
}
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)
{
@@ -109,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()
@@ -117,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)
@@ -131,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);
@@ -149,27 +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)
{
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
var status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
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, "");
+16 -3
View File
@@ -1,5 +1,4 @@
using NStandard;
using System;
using System;
using System.Threading.Tasks;
namespace LgTv
@@ -28,7 +27,21 @@ namespace LgTv
_8,
_9,
_0,
POWER
POWER,
VOLUMEUP,
VOLUMEDOWN,
MUTE,
MENU,
CC,
DASH,
CHANNELUP,
CHANNELDOWN,
LIST,
AD,
SAP,
PROGRAM,
PLAY,
PAUSE
}
public class LgWebOsMouseService : IDisposable
+6 -2
View File
@@ -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; }
+11 -10
View File
@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Native" version="0.1.0" targetFramework="net472" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
<package id="NLog" version="4.7.2" targetFramework="net472" />
<package id="NStandard" version="0.4.3" targetFramework="net472" />
<package id="NvAPIWrapper.Net" version="0.8.0.98" targetFramework="net472" />
<package id="NWin32" version="1.0.6" targetFramework="net472" />
<package id="PacketDotNet" version="1.0.5" targetFramework="net472" />
<package id="SharpPcap" version="5.2.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.10" targetFramework="net472" />
<package id="NStandard" version="0.8.9" targetFramework="net472" />
<package id="NvAPIWrapper.Net" version="0.8.1.101" targetFramework="net472" />
<package id="NWin32" version="1.2.0" targetFramework="net472" />
<package id="PacketDotNet" version="1.2.0" targetFramework="net472" />
<package id="SharpPcap" version="5.4.0" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.7.0" targetFramework="net472" />
<package id="TaskScheduler" version="2.8.20" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net472" />
<package id="TaskScheduler" version="2.9.1" targetFramework="net472" />
</packages>
+6
View File
@@ -9,6 +9,12 @@ And if you own a recent LG TV, you can control your TV through the app (no NVIDI
![Screenshot2](https://github.com/Maassoft/ColorControl/blob/master/images/LgController.png)
For this to work correctly:
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.
On the Options-tabpage you can finetune some parameters and/or enable some settings:
![Screenshot3](https://github.com/Maassoft/ColorControl/blob/master/images/Options.png)
+39
View File
@@ -0,0 +1,39 @@
using System.ComponentModel;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace System.Net
{
/// <summary>Stellt Methoden für das Senden von Anfragen über das ARP-Protokoll bereit.</summary>
public static class ArpRequest
{
/// <summary>
/// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben.
/// </summary>
/// <param name="destination">Destination <see cref="IPAddress"/>.</param>
/// <returns>Eine <see cref="T:System.Net.ArpRequestResult">ArpRequestResult</see>-Instanz, welche die Ergebnisse der Anfrage enthält.</returns>
public static ArpRequestResult Send(IPAddress destination)
{
if (destination == null)
throw new ArgumentNullException(nameof(destination));
int destIp = BitConverter.ToInt32(destination.GetAddressBytes(), 0);
var addr = new byte[6];
var len = addr.Length;
var res = NativeMethods.SendARP(destIp, 0, addr, ref len);
if (res == 0)
return new ArpRequestResult(new PhysicalAddress(addr));
return new ArpRequestResult(new Win32Exception(res));
}
/// <summary>
/// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben.
/// </summary>
/// <param name="destination">Destination <see cref="IPAddress"/>.</param>
/// <returns>Ein asynchroner Task, welcher einen ARP-Request sendet.</returns>
public static Task<ArpRequestResult> SendAsync(IPAddress destination) => Task.Run(() => Send(destination));
}
}
+57
View File
@@ -0,0 +1,57 @@
using System.Net.NetworkInformation;
using System.Text;
namespace System.Net
{
// TODO: rethink the whole exception thing
/// <summary>
/// Enthält die Rückgabewerte der ArpRequest.Send-Funktion.
/// </summary>
public class ArpRequestResult
{
/// <summary>Falls Fehler bei der Protokollanfrage auftreten, werden diese in dieser Eigenschaft abgelegt. Andernfalls null.</summary>
public Exception Exception { get; }
/// <summary>Die aufgelöste physikalische Adresse.</summary>
public PhysicalAddress Address { get; }
/// <summary>Erstellt eine neue ArpRequestResult-Instanz</summary>
/// <param name="address">Die physikalische Adresse</param>
public ArpRequestResult(PhysicalAddress address)
{
this.Exception = null;
Address = address;
}
/// <summary>Erstellt eine neue ArpRequestResult-Instanz</summary>
/// <param name="exception">Der aufgetretene Fehler</param>
public ArpRequestResult(Exception exception)
{
this.Exception = exception;
Address = null;
}
/// <summary>Konvertiert ARP-Rückgabewerte in eine Zeichenfolge.</summary>
public override string ToString()
{
var sb = new StringBuilder();
if (Address == null)
sb.Append("no address");
else
{
sb.Append("address: ");
sb.Append(Address);
}
sb.Append(", ");
if (Exception == null)
sb.Append("no exception");
else
{
sb.Append("exception: ");
sb.Append(Exception.Message);
}
return sb.ToString();
}
}
}
+62
View File
@@ -0,0 +1,62 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
//
// To add a suppression to this file, right-click the message in the
// Code Analysis results, point to "Suppress Message", and click
// "In Suppression File".
// You do not need to add suppressions to this file manually.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Int32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Int32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Int32,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[])")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Int32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Int32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Int32,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[])")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte[])")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte[],System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte[])")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte[],System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lan")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "System.Net.Topology.INetMask.#GetMaskBytes()")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_Equality(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_Inequality(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_BitwiseAnd(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "System.Net.Topology.SiblingOptions")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "type", Target = "System.Net.SendWol")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_BitwiseOr(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#BitwiseOr(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint,System.Net.SecureOnPassword)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cidr", Scope = "member", Target = "System.Net.Topology.INetMask.#Cidr")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Byte,System.Byte,System.Byte,System.Byte)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#BitwiseAnd(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "cidr", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Int32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "cidr", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Byte)")]
+166
View File
@@ -0,0 +1,166 @@
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace System.Net
{
/// <summary>Provides extension methods for sending Wake On LAN signals (magic packets) to a specific <see cref="IPAddress"/>.</summary>
public static class IPAddressExtensions
{
private const int DefaultWolPort = 7;
#region Wol
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="port">The port to send the packet to.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, byte[] macAddress, int port) => target.SendWol(macAddress, port, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="port">The port to send the packet to.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, byte[] macAddress, int port, SecureOnPassword password)
{
Net.MagicPacket.Send(new IPEndPoint(target, port), macAddress, password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, byte[] macAddress) => target.SendWol(macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, byte[] macAddress, SecureOnPassword password)
{
Net.MagicPacket.Send(new IPEndPoint(target, DefaultWolPort), macAddress, password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="PhysicalAddress" /> <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, PhysicalAddress macAddress) => target.SendWol(macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="PhysicalAddress" /> <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void SendWol(this IPAddress target, PhysicalAddress macAddress, SecureOnPassword password)
{
Net.MagicPacket.Send(new IPEndPoint(target, DefaultWolPort), macAddress, password);
}
#endregion
#region ARP
/// <summary>
/// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben.
/// </summary>
/// <param name="destination">Destination <see cref="IPAddress"/>.</param>
/// <returns>Eine <see cref="T:System.Net.ArpRequestResult">ArpRequestResult</see>-Instanz, welche die Ergebnisse der Anfrage enthält.</returns>
public static ArpRequestResult SendArpRequest(this IPAddress destination) => ArpRequest.Send(destination);
#endregion
#region TAP
#region Wol
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="port">The port to send the packet to.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, byte[] macAddress, int port) => target.SendWolAsync(macAddress, port, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="port">The port to send the packet to.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, byte[] macAddress, int port, SecureOnPassword password)
{
return Net.MagicPacket.SendAsync(new IPEndPoint(target, port), macAddress, password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, byte[] macAddress) => target.SendWolAsync(macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, byte[] macAddress, SecureOnPassword password)
{
return Net.MagicPacket.SendAsync(new IPEndPoint(target, DefaultWolPort), macAddress, password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="PhysicalAddress" /> <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, PhysicalAddress macAddress) => target.SendWolAsync(macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="PhysicalAddress" /> <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPAddress target, PhysicalAddress macAddress, SecureOnPassword password)
{
return Net.MagicPacket.SendAsync(new IPEndPoint(target, DefaultWolPort), macAddress, password);
}
#endregion
#region ARP
/// <summary>Sends a request via ARP to resolve an IP address to aphysical address. If the physical address is already cached, it's cached value is returned.</summary>
/// <param name="destination">Destination <see cref="IPAddress"/>.</param>
/// <returns>An asynchronous <see cref="Task"/> which sends an ARP request.</returns>
public static Task<ArpRequestResult> SendArpRequestAsync(this IPAddress destination) => ArpRequest.SendAsync(destination);
#endregion
#endregion
}
}
+101
View File
@@ -0,0 +1,101 @@
using System.Threading.Tasks;
using System.Net.NetworkInformation;
namespace System.Net
{
/// <summary>Provides extension methods for sending Wake On LAN signals (magic packets) to a specific <see cref="IPEndPoint"/>.</summary>
public static class IPEndPointExtensions
{
#region Wol
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array macAddress is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
public static void SendWol(this IPEndPoint target, byte[] macAddress)
{
Net.MagicPacket.Send(target, macAddress);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
public static void SendWol(this IPEndPoint target, PhysicalAddress macAddress)
{
Net.MagicPacket.Send(target, macAddress);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array macAddress is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
public static void SendWol(this IPEndPoint target, byte[] macAddress, SecureOnPassword password)
{
Net.MagicPacket.Send(target, macAddress, password);
}
/// <summary>
/// Sendet ein Wake-On-LAN-Signal an einen Client.
/// </summary>
/// <param name="target">Der Ziel-IPEndPoint.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
public static void SendWol(this IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password)
{
Net.MagicPacket.Send(target, macAddress, password);
}
#endregion
#region TAP
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array macAddress is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPEndPoint target, byte[] macAddress)
{
return Net.MagicPacket.SendAsync(target, macAddress);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPEndPoint target, PhysicalAddress macAddress)
{
return Net.MagicPacket.SendAsync(target, macAddress);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array macAddress is not 6.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPEndPoint target, byte[] macAddress, SecureOnPassword password)
{
return Net.MagicPacket.SendAsync(target, macAddress, password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password)
{
return Net.MagicPacket.SendAsync(target, macAddress, password);
}
#endregion
}
}
+12
View File
@@ -0,0 +1,12 @@
#define GERMAN
namespace System.Net
{
internal static class Localization
{
public const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length.";
public const string ArgumentExceptionInvalidPasswordLength = "Invalid password length.";
public const string ArgumentExceptionIsNotAValidMadAddress = " is not a valid MAC address.";
}
}
+171
View File
@@ -0,0 +1,171 @@
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace System.Net
{
/// <summary>Provides methods for sending Wake On LAN signals (magic packets).</summary>
public static class MagicPacket
{
#region Wol
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <exception cref="ArgumentNullException">target is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array macAddress is not 6.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void Send(IPEndPoint target, byte[] macAddress) => Send(target, macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void Send(IPEndPoint target, byte[] macAddress, SecureOnPassword password)
{
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
byte[] passwordBuffer = password?.GetPasswordBytes();
byte[] packet = GetWolPacket(macAddress, passwordBuffer);
SendPacket(target, packet);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void Send(IPEndPoint target, PhysicalAddress macAddress) => Send(target, macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="SocketException">An error occurred when accessing the socket. See Remarks section of <see cref="UdpClient.Send(byte[], int, IPEndPoint)"/> for more information.</exception>
public static void Send(IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password)
{
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
byte[] passwordBuffer = password?.GetPasswordBytes();
byte[] packet = GetWolPacket(macAddress.GetAddressBytes(), passwordBuffer);
SendPacket(target, packet);
}
private static void SendPacket(IPEndPoint target, byte[] packet)
{
using (var cl = new UdpClient())
cl.Send(packet, packet.Length, target);
}
#endregion
#region TAP
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendAsync(IPEndPoint target, byte[] macAddress) => SendAsync(target, macAddress);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendAsync(IPEndPoint target, byte[] macAddress, SecureOnPassword password)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
var passwordBuffer = password?.GetPasswordBytes();
var packet = GetWolPacket(macAddress, passwordBuffer);
return SendPacketAsync(target, packet);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendAsync(IPEndPoint target, PhysicalAddress macAddress) => SendAsync(target, macAddress, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a client.</summary>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="macAddress">The MAC address of the designated client.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendAsync(IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
var passwordBuffer = password?.GetPasswordBytes();
var p = GetWolPacket(macAddress.GetAddressBytes(), passwordBuffer);
return SendPacketAsync(target, p);
}
private static Task SendPacketAsync(IPEndPoint target, byte[] packet)
{
var cl = new UdpClient();
return cl.SendAsync(packet, packet.Length, target).ContinueWith((Task t) => cl.Close());
}
#endregion
#region internal
/// <exception cref="ArgumentNullException"><paramref name="macAddress"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="macAddress"/> is not 6.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array <paramref name="password"/> is not 0 or 6.</exception>
private static byte[] GetWolPacket(byte[] macAddress, byte[] password)
{
if (macAddress == null)
throw new ArgumentNullException(nameof(macAddress));
if (macAddress.Length != 6)
throw new ArgumentException(Localization.ArgumentExceptionInvalidMacAddressLength);
password = password ?? new byte[0];
if (password.Length != 0 && password.Length != 6)
throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength);
var packet = new byte[17 * 6 + password.Length];
int offset, i;
for (offset = 0; offset < 6; ++offset)
packet[offset] = 0xFF;
for (offset = 6; offset < 17 * 6; offset += 6)
for (i = 0; i < 6; ++i)
packet[i + offset] = macAddress[i];
if (password.Length > 0)
{
for (offset = 16 * 6 + 6; offset < (17 * 6 + password.Length); offset += 6)
for (i = 0; i < 6; ++i)
packet[i + offset] = password[i];
}
return packet;
}
#endregion
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.Runtime.InteropServices;
using System.Security;
namespace System.Net
{
internal static class NativeMethods
{
private const string IphlpApi = "iphlpapi.dll";
[DllImport(IphlpApi, ExactSpelling = true)]
[SecurityCritical]
internal static extern int SendARP(int destinationIp, int sourceIp, byte[] macAddress, ref int physicalAddrLength);
}
}
+148
View File
@@ -0,0 +1,148 @@
using System.Threading.Tasks;
using System.Net.NetworkInformation;
namespace System.Net
{
/// <summary>Provides extension methods for sending Wake On LAN signals (magic packets) using a specific <see cref="PhysicalAddress"/>.</summary>
public static class PhysicalAddressExtensions
{
#region Wol
/// <summary>Sends a Wake On LAN signal (magic packet) to the broadcast IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static void SendWol(this PhysicalAddress address) => address.SendWol(IPAddress.Broadcast, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static void SendWol(this PhysicalAddress address, IPAddress target) => address.SendWol(target, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static void SendWol(this PhysicalAddress address, IPAddress target, SecureOnPassword password)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
target.SendWol(address.GetAddressBytes(), password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static void SendWol(this PhysicalAddress address, IPEndPoint target) => address.SendWol(target, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static void SendWol(this PhysicalAddress address, IPEndPoint target, SecureOnPassword password)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
target.SendWol(address.GetAddressBytes(), password);
}
#endregion
#region TAP
/// <summary>Sends a Wake On LAN signal (magic packet) to the broadcast IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
public static Task SendWolAsync(this PhysicalAddress address)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
return IPAddress.Broadcast.SendWolAsync(address.GetAddressBytes());
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this PhysicalAddress address, IPAddress target) => address.SendWolAsync(target, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPAddress"/>.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this PhysicalAddress address, IPAddress target, SecureOnPassword password)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
return target.SendWolAsync(address.GetAddressBytes(), password);
}
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this PhysicalAddress address, IPEndPoint target) => address.SendWolAsync(target, null);
/// <summary>Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address.</summary>
/// <param name="address">The instance of the physical address that should be used in the magic packet.</param>
/// <param name="target">Destination <see cref="IPEndPoint"/>.</param>
/// <param name="password">The SecureOn password of the client.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>An asynchronous <see cref="Task"/> which sends a Wake On LAN signal (magic packet) to a client.</returns>
public static Task SendWolAsync(this PhysicalAddress address, IPEndPoint target, SecureOnPassword password)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
return target.SendWolAsync(address.GetAddressBytes(), password);
}
#endregion
#region common
/// <summary>Gets the type (<see cref="PhysicalAddressAdministrator"/>) of the <see cref="PhysicalAddress" />.</summary>
/// <param name="address">The address.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>The <see cref="PhysicalAddressType" /> of the physical addess (MAC address).</returns>
public static PhysicalAddressType GetAddressType(this PhysicalAddress address)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
var bytes = address.GetAddressBytes();
if (bytes == null || bytes.Length < 1)
throw new ArgumentException($"Invalid {nameof(address)}.");
return (bytes[0] & 0x1) == 0 ? PhysicalAddressType.Unicast : PhysicalAddressType.Multicast;
}
/// <summary>Gets the administrator (<see cref="PhysicalAddressAdministrator"/>) of the <see cref="PhysicalAddress" />.</summary>
/// <param name="address">The address.</param>
/// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception>
/// <returns>The <see cref="PhysicalAddressAdministrator" /> of the physical addess (MAC address).</returns>
public static PhysicalAddressAdministrator GetAddressAdministrator(this PhysicalAddress address)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
var bytes = address.GetAddressBytes();
if (bytes == null || bytes.Length < 1)
throw new ArgumentException($"Invalid {nameof(address)}.");
return (bytes[0] & 0x2) == 0 ? PhysicalAddressAdministrator.Global : PhysicalAddressAdministrator.Local;
}
#endregion
}
}
+34
View File
@@ -0,0 +1,34 @@
namespace System.Net
{
/// <summary>
/// Der Administrator der physikalischen Adresse gibt an, ob die Adresse globally unique oder local administrated ist.
/// </summary>
public enum PhysicalAddressAdministrator
{
/// <summary>
/// Die Adresse ist global einzigartig (nach der OUI).
/// </summary>
Global,
/// <summary>
/// Die Adresse ist lokal administriert.
/// </summary>
Local
}
/// <summary>
/// Der Typ der physikalischen Adresse gibt an, ob es um eine UNicast oder Multicast-Adresse handelt.
/// </summary>
public enum PhysicalAddressType
{
/// <summary>
/// Bezeichnet eine Unicast-Adresse.
/// </summary>
Unicast,
/// <summary>
/// Bezeichnet eine Multicast-Adresse
/// </summary>
Multicast
}
}
+4
View File
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
using System;
[assembly: InternalsVisibleTo("WakeOnLan.Tests")]
+95
View File
@@ -0,0 +1,95 @@
namespace System.Net
{
/// <summary>Provides a SecureOn password.</summary>
[Serializable]
public sealed class SecureOnPassword
{
private readonly byte[] _password;
/// <summary>Initializes a new instance of <see cref="SecureOnPassword"/> with the given password.</summary>
/// <param name="password">The password as <see cref="Byte"/> array.</param>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <exception cref="ArgumentException">The length of the <see cref="T:System.Byte" /> array password is not 6.</exception>
public SecureOnPassword(byte[] password)
{
if (password == null)
throw new ArgumentNullException(nameof(password));
if (password.Length != 6)
throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength);
_password = password;
}
/// <summary>Gets the buffer of the password.</summary>
public byte[] GetPasswordBytes()
{
if (_password == null)
return null;
var buffer = new byte[_password.Length];
Array.Copy(_password, buffer, 0);
return buffer;
}
/// <summary>Initializes a new instance of <see cref="SecureOnPassword"/> with the given password.</summary>
/// <param name="password">The password as <see cref="String"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <remarks>Uses <see cref="Text.Encoding.Default" /> as encoding.</remarks>
public SecureOnPassword(string password)
: this(password, Text.Encoding.Default)
{ }
/// <summary>Initializes a new instance of <see cref="SecureOnPassword"/> with the given password.</summary>
/// <param name="password">The password as <see cref="String"/>.</param>
/// <param name="encoding">The <see cref="Text.Encoding"/> instance to use for the password.</param>
/// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="encoding"/> is null.</exception>
/// <exception cref="ArgumentException">The <see cref="Byte"/> array wich is created using the password has more elements than 6.</exception>
public SecureOnPassword(string password, Text.Encoding encoding)
{
if (password == null)
throw new ArgumentNullException(nameof(password));
if (encoding == null)
throw new ArgumentNullException(nameof(encoding));
if (string.IsNullOrEmpty(password))
_password = new byte[6];
var bytes = encoding.GetBytes(password);
if (bytes.Length > 6)
throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength);
_password = new byte[6];
for (int i = 0; i < bytes.Length; i++)
_password[i] = bytes[i];
if (bytes.Length < 6)
{
for (int i = bytes.Length - 1; i < 6; i++)
_password[i] = 0x00;
}
}
/// <summary>Converts the <see cref="SecureOnPassword"/> to dash notation.</summary>
/// <returns>A <see cref="String"/> representing the <see cref="SecureOnPassword"/> as dash notation.</returns>
public override string ToString() => ToString("X2");
/// <summary>Converts the <see cref="SecureOnPassword"/> to dash notation.</summary>
/// <returns>A <see cref="String"/> representing the <see cref="SecureOnPassword"/> as dash notation.</returns>
private string ToString(string format)
{
var f = new string[6];
for (int i = 0; i < f.Length; i++)
f[i] = _password[i].ToString(format);
return string.Join("-", f);
}
/// <summary>Converts the <see cref="SecureOnPassword"/> to dash notation.</summary>
/// <returns>A <see cref="String"/> representing the <see cref="SecureOnPassword"/> as dash notation.</returns>
public string ToString(IFormatProvider format)
{
var f = new string[6];
for (int i = 0; i < f.Length; i++)
f[i] = _password[i].ToString(format);
return string.Join("-", f);
}
}
}
+287
View File
@@ -0,0 +1,287 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace System.Net.Topology
{
internal static class ByteArrayExtensions
{
internal static IEnumerable<bool> ToBitStream(this byte[] bytes, bool fromLeft)
{
if (bytes == null)
throw new ArgumentNullException(nameof(bytes));
if (fromLeft)
{
for (int i = 0; i < bytes.Length; ++i)
{
byte tmp = bytes[i].ReverseBits();
for (int j = 0; j < 8; ++j)
{
yield return (tmp & 1) == 1;
tmp >>= 1;
}
}
}
else
{
for (int i = bytes.Length - 1; i >= 0; --i)
{
byte tmp = bytes[i];
for (int j = 0; j < 8; ++j)
{
yield return (tmp & 1) == 1;
tmp >>= 1;
}
}
}
}
private static int CountFromSide(byte[] bits, bool value, bool fromleft)
{
int counter = 0;
var str = bits.ToBitStream(fromleft);
foreach (var bit in str)
{
if (bit == value)
++counter;
else return counter;
}
return counter;
}
internal static int CountFromLeft(this byte[] bits, bool value)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
return CountFromSide(bits, value, true);
}
internal static int CountFromRight(this byte[] bits, bool value)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
return CountFromSide(bits, value, false);
}
internal static string ToBinaryString(this byte[] bits, char separator)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
const int radix = 2;
const int padding = 8;
const char paddingChar = '0';
var sb = new StringBuilder();
sb.Append(Convert.ToString(bits[0], radix).PadLeft(padding, paddingChar)).Append(separator);
sb.Append(Convert.ToString(bits[1], radix).PadLeft(padding, paddingChar)).Append(separator);
sb.Append(Convert.ToString(bits[2], radix).PadLeft(padding, paddingChar)).Append(separator);
sb.Append(Convert.ToString(bits[3], radix).PadLeft(padding, paddingChar));
return sb.ToString();
}
internal static string ToBinaryString(this byte[] bits)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
const int radix = 2;
const int padding = 8;
const char paddingChar = '0';
var sb = new StringBuilder();
sb.Append(Convert.ToString(bits[0], radix).PadLeft(padding, paddingChar));
sb.Append(Convert.ToString(bits[1], radix).PadLeft(padding, paddingChar));
sb.Append(Convert.ToString(bits[2], radix).PadLeft(padding, paddingChar));
sb.Append(Convert.ToString(bits[3], radix).PadLeft(padding, paddingChar));
return sb.ToString();
}
internal static bool RepresentsValidNetMask(this byte[] bits)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
int fromLeft = bits.CountFromLeft(true);
int fromRight = bits.CountFromRight(false);
// Sum of all counted indexes schloud be equal the whole length
return (fromLeft + fromRight) == (8 * NetMask.MaskLength);
}
internal static byte[] And(this byte[] b1, byte[] b2)
{
if (b1 == null)
throw new ArgumentNullException(nameof(b1));
if (b2 == null)
throw new ArgumentNullException(nameof(b2));
if (b1.Length == 1 && b2.Length == 1)
{
int ib1 = b1[0];
int ib2 = b2[0];
return new[] { (byte)(ib1 & ib2) };
}
if (b1.Length == 2 && b2.Length == 2)
{
var sb1 = BitConverter.ToInt16(b1, 0);
var sb2 = BitConverter.ToInt16(b2, 0);
return BitConverter.GetBytes((short)(sb1 & sb2));
}
if (b1.Length == 4 && b2.Length == 4)
{
var ib1 = BitConverter.ToInt32(b1, 0);
var ib2 = BitConverter.ToInt32(b2, 0);
return BitConverter.GetBytes(ib1 & ib2);
}
if (b1.Length != b2.Length)
{
// Or maybe throw exception?
int maxIndex = Math.Max(b1.Length, b2.Length);
byte[] biggerArray = b1.Length > b2.Length ? b1 : b2;
byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2;
var paddedArray = new byte[maxIndex];
Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length);
Debug.Assert(biggerArray.Length == paddedArray.Length);
return And(biggerArray, paddedArray);
}
var targetIndex = b1.Length;
var andedArray = new byte[targetIndex];
Buffer.BlockCopy(b1, 0, andedArray, 0, andedArray.Length);
for (int i = 0; i < targetIndex; ++i)
andedArray[i] &= b2[i];
return andedArray;
}
internal static byte[] Or(this byte[] b1, byte[] b2)
{
if (b1 == null)
throw new ArgumentNullException(nameof(b1));
if (b2 == null)
throw new ArgumentNullException(nameof(b2));
if (b1.Length == 1 && b2.Length == 1)
{
int ib1 = b1[0];
int ib2 = b2[0];
return new[] { (byte)(ib1 | ib2) };
}
if (b1.Length == 2 && b2.Length == 2)
{
var sb1 = BitConverter.ToInt16(b1, 0);
var sb2 = BitConverter.ToInt16(b2, 0);
return BitConverter.GetBytes((short)(sb1 | sb2));
}
if (b1.Length == 4 && b2.Length == 4)
{
var ib1 = BitConverter.ToInt32(b1, 0);
var ib2 = BitConverter.ToInt32(b2, 0);
return BitConverter.GetBytes(ib1 | ib2);
}
if (b1.Length != b2.Length)
{
// Or maybe throw exception?
int maxIndex = Math.Max(b1.Length, b2.Length);
byte[] biggerArray = b1.Length > b2.Length ? b1 : b2;
byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2;
var paddedArray = new byte[maxIndex];
Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length);
Debug.Assert(biggerArray.Length == paddedArray.Length);
return Or(biggerArray, paddedArray);
}
var targetIndex = b1.Length;
var oredArray = new byte[targetIndex];
Buffer.BlockCopy(b1, 0, oredArray, 0, oredArray.Length);
for (int i = 0; i < targetIndex; ++i)
oredArray[i] |= b2[i];
return oredArray;
}
internal static byte[] Xor(this byte[] b1, byte[] b2)
{
if (b1 == null)
throw new ArgumentNullException(nameof(b1));
if (b2 == null)
throw new ArgumentNullException(nameof(b2));
// TODO: Testing
if (b1.Length == 1 && b2.Length == 1)
{
int ib1 = b1[0];
int ib2 = b2[0];
return new[] { (byte)(ib1 ^ ib2) };
}
if (b1.Length == 2 && b2.Length == 2)
{
var sb1 = BitConverter.ToInt16(b1, 0);
var sb2 = BitConverter.ToInt16(b2, 0);
return BitConverter.GetBytes((short)(sb1 ^ sb2));
}
if (b1.Length == 4 && b2.Length == 4)
{
var ib1 = BitConverter.ToInt32(b1, 0);
var ib2 = BitConverter.ToInt32(b2, 0);
return BitConverter.GetBytes(ib1 ^ ib2);
}
if (b1.Length != b2.Length)
{
// Or maybe throw exception?
int maxIndex = Math.Max(b1.Length, b2.Length);
byte[] biggerArray = b1.Length > b2.Length ? b1 : b2;
byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2;
var paddedArray = new byte[maxIndex];
Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length);
Debug.Assert(biggerArray.Length == paddedArray.Length);
return Xor(biggerArray, paddedArray);
}
var targetIndex = b1.Length;
var xoredArray = new byte[targetIndex];
Buffer.BlockCopy(b1, 0, xoredArray, 0, xoredArray.Length);
for (int i = 0; i < targetIndex; ++i)
xoredArray[i] ^= b2[i];
return xoredArray;
}
internal static byte[] Not(this byte[] bits)
{
if (bits == null)
throw new ArgumentNullException(nameof(bits));
if (bits.Length == 4)
{
var i = BitConverter.ToInt32(bits, 0);
return BitConverter.GetBytes(~i);
}
var newBytes = new byte[bits.Length];
for (int i = 0; i < newBytes.Length; ++i)
newBytes[i] = unchecked((byte)(~bits[i]));
return newBytes;
}
}
}
+43
View File
@@ -0,0 +1,43 @@
namespace System.Net.Topology
{
internal static class ByteExtensions
{
public static byte[] BitReverseTable = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
public static byte ReverseBits(this byte toReverse) => BitReverseTable[toReverse];
}
}
+17
View File
@@ -0,0 +1,17 @@
namespace System.Net.Topology
{
/// <summary>Provides an interface for IP net masks.</summary>
public interface INetMask
{
/// <summary>Gets the length of the net mask in bits.</summary>
int AddressLength { get; }
/// <summary>Gets the amount of set bits from the left side (used in CIDR-Notation of net masks).</summary>
int Cidr { get; }
/// <summary>Gets the bits of the net mask instance as an BitArray object instance.</summary>
/// <returns>The bits of the net mask instance as an BitArray object instance</returns>
byte[] GetMaskBytes();
}
}
+167
View File
@@ -0,0 +1,167 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace System.Net.Topology
{
/// <summary>Provides extension methods for the <see cref="T:System.Net.IPAddress"/>.</summary>
public static class IPAddressExtensions
{
private const string OnlyIPv4Supported = "Only IPv4 is currently supported";
/// <summary>Enumerates through the siblings of an <see cref="T:System.Net.IPAddress"/> in a network. Compliant to RFC 950 (2^n-2).</summary>
/// <param name="address">The address</param>
/// <param name="mask">The net mask of the network</param>
public static IEnumerable<IPAddress> GetSiblings(this IPAddress address, NetMask mask)
{
return GetSiblings(address, mask, SiblingOptions.ExcludeUnusable);
}
/// <summary>Enumerates through the siblings of an <see cref="T:System.Net.IPAddress"/> in a network.</summary>
/// <param name="address">The address</param>
/// <param name="mask">The net mask of the network</param>
/// <param name="options">Options which addresses to include an which not</param>
public static IEnumerable<IPAddress> GetSiblings(this IPAddress address, NetMask mask, SiblingOptions options)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
if (mask == null)
throw new ArgumentNullException(nameof(mask));
if (address.AddressFamily != Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException(OnlyIPv4Supported);
bool includeSelf = BitHelper.IsOptionSet(options, SiblingOptions.IncludeSelf);
bool includeBroadcast = BitHelper.IsOptionSet(options, SiblingOptions.IncludeBroadcast);
bool includeNetworkIdentifier = BitHelper.IsOptionSet(options, SiblingOptions.IncludeNetworkIdentifier);
bool alreadyReturnedSelf = false;
var netPrefix = address.GetNetworkPrefix(mask);
if (includeNetworkIdentifier)
{
netPrefix = address.GetNetworkPrefix(mask);
if (netPrefix.Equals(address))
alreadyReturnedSelf = true;
yield return netPrefix;
}
var selfAddressBytes = address.GetAddressBytes();
var netPrefixBytes = netPrefix.GetAddressBytes();
int cidr = mask.Cidr;
uint maxHosts = 0xFFFFFFFF;
if (cidr > 0)
maxHosts = (uint)(1 << (8 * NetMask.MaskLength - cidr)) - 1;
var hostBytes = new byte[NetMask.MaskLength];
for (int hostPart = 1; hostPart < maxHosts; ++hostPart)
{
unchecked
{
hostBytes[0] = (byte)(hostPart >> 24);
hostBytes[1] = (byte)(hostPart >> 16);
hostBytes[2] = (byte)(hostPart >> 8);
hostBytes[3] = (byte)(hostPart >> 0);
}
Debug.WriteLine("HostPart: " + hostPart.ToString("X2").PadLeft(8, '0') + " (" + BitConverter.ToString(hostBytes) + ")");
var nextIpBytes = netPrefixBytes.Or(hostBytes);
var nextIp = new IPAddress(nextIpBytes);
if (!alreadyReturnedSelf)
{
if (includeSelf)
{
if (nextIpBytes[0] == selfAddressBytes[0]
&& nextIpBytes[1] == selfAddressBytes[1]
&& nextIpBytes[2] == selfAddressBytes[2]
&& nextIpBytes[3] == selfAddressBytes[3])
alreadyReturnedSelf = true;
yield return nextIp;
}
else if (nextIpBytes[0] != selfAddressBytes[0]
|| nextIpBytes[1] != selfAddressBytes[1]
|| nextIpBytes[2] != selfAddressBytes[2]
|| nextIpBytes[3] != selfAddressBytes[3])
yield return nextIp;
}
else
yield return nextIp;
}
if (includeBroadcast)
{
var broadcastAddress = address.GetBroadcastAddress(mask);
if (!address.Equals(broadcastAddress) || (address.Equals(broadcastAddress) && !alreadyReturnedSelf))
yield return broadcastAddress;
}
}
/// <summary>Gets the network prefix of an <see cref="T:System.Net.IPAddress"/>.</summary>
/// <param name="address">The address</param>
/// <param name="mask">The net mask of the network</param>
/// <returns>The network prefix of an <see cref="T:System.Net.IPAddress"/></returns>
public static IPAddress GetNetworkPrefix(this IPAddress address, NetMask mask)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
if (mask == null)
throw new ArgumentNullException(nameof(mask));
if (address.AddressFamily != Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException(OnlyIPv4Supported);
return mask & address;
}
/// <summary>Gets the broadcast address of an <see cref="T:System.Net.IPAddress"/>.</summary>
/// <param name="address">The address</param>
/// <param name="mask">The net mask of the network</param>
/// <returns>The broadcast address of an <see cref="T:System.Net.IPAddress"/></returns>
public static IPAddress GetBroadcastAddress(this IPAddress address, NetMask mask)
{
if (address == null)
throw new ArgumentNullException("address");
if (mask == null)
throw new ArgumentNullException("mask");
if (address.AddressFamily != Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException(OnlyIPv4Supported);
// TODO: Test
var ipBytes = address.GetAddressBytes();
var notMaskBytes = mask.GetMaskBytes().Not();
var broadcastAddressBytes = notMaskBytes.Or(ipBytes);
return new IPAddress(broadcastAddressBytes);
}
/// <summary>Gets the host identifier (rest) an <see cref="T:System.Net.IPAddress"/>.</summary>
/// <param name="address">The address</param>
/// <param name="mask">The net mask of the network</param>
/// <returns>The host identifier (rest) an <see cref="T:System.Net.IPAddress"/></returns>
public static IPAddress GetHostIdentifier(this IPAddress address, NetMask mask)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
if (mask == null)
throw new ArgumentNullException(nameof(mask));
if (address.AddressFamily != Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException(OnlyIPv4Supported);
var maskBits = mask.GetMaskBytes();
var ipBits = address.GetAddressBytes();
// ~Mask & IP
var retVal = maskBits.Not().And(ipBits);
var bytes = new byte[NetMask.MaskLength];
Buffer.BlockCopy(retVal, 0, bytes, 0, bytes.Length);
return new IPAddress(bytes);
}
}
}
+312
View File
@@ -0,0 +1,312 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Net.Topology
{
// TODO: Use Span<T> when available (instead of byte arrays)
/// <summary>Represents an IPv4 net mask.</summary>
[StructLayout(LayoutKind.Explicit)]
public struct NetMask : INetMask, IEquatable<NetMask>
{
[FieldOffset(0)]
private readonly uint _mask;
#region byte-wise fields
// We are assuming here, that the byte-ordering is like on a normal x86 system
[FieldOffset(3)]
private readonly byte _maskB0;
[FieldOffset(2)]
private readonly byte _maskB1;
[FieldOffset(1)]
private readonly byte _maskB2;
[FieldOffset(0)]
private readonly byte _maskB3;
#endregion
internal const int MaskLength = 4;
/// <summary>Represents an empty IPv4 NetMask (all bits set to 0).</summary>
public static NetMask Empty { get; } = new NetMask();
/// <summary>Gets the length of the net mask in bits.</summary>
public int AddressLength => MaskLength * 8;
/// <summary>Gets the amount of set bits from the left side (used in CIDR-Notation of net masks).</summary>
public int Cidr => GetCidr(_mask);
#region Ctors
/// <summary>Creates a new instance of <see cref="T:System.Net.Topology.NetMask"/> cloning an existing instance of <see cref="T:System.Net.Topology.NetMask"/>.</summary>
public NetMask(NetMask mask)
{
if (mask == null)
throw new ArgumentNullException(nameof(mask));
_maskB0 = _maskB1 = _maskB2 = _maskB3 = 0;
_mask = mask._mask;
}
/// <summary>Creates a new instance of <see cref="T:System.Net.Topology.NetMask"/> from an array of <see cref="System.Byte"/>.</summary>
public NetMask(byte[] value)
{
_maskB0 = _maskB1 = _maskB2 = _maskB3 = 0;
_mask = 0;
if (value == null || value.Length == 0) // maybe throw ArgumentNullException?
return;
if (value.Length != MaskLength)
throw new ArgumentException("Invalid mask length.");
CheckMaskBytes(value); // check if passed mask are a valid mask. if not, throw Exception
_maskB0 = value[0];
_maskB1 = value[1];
_maskB2 = value[2];
_maskB3 = value[3];
}
/// <summary>Creates a new instance of <see cref="T:System.Net.Topology.NetMask"/> from a given <see cref="T:System.Net.IPAddress"/>.</summary>
/// <param name="address">The IPv4 address.</param>
public NetMask(IPAddress address)
: this(address == null ? null : address.GetAddressBytes())
{ }
/// <summary>Creates a new instance of <see cref="T:System.Net.Topology.NetMask"/>.</summary>
/// <param name="m0">The first byte.</param>
/// <param name="m1">The second byte.</param>
/// <param name="m2">The third byte.</param>
/// <param name="m3">The fourth byte.</param>
public NetMask(byte m0, byte m1, byte m2, byte m3)
: this(new[] { m0, m1, m2, m3 })
{ }
/// <summary>Creates a new instance of <see cref="T:System.Net.Topology.NetMask"/>.</summary>
/// <param name="cidr">The mask represented by the CIDR notation integer.</param>
public NetMask(byte cidr)
{
// maybe change parameter type interpretation to CIDR?
if (cidr > MaskLength * 8)
throw new ArgumentException("Invalid CIDR length");
_maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; // to keep init checks happy
_mask = GetUIntFromCidrValue(cidr);
}
private NetMask(uint ipv4Mask)
{
_maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; // to keep init checks happy
_mask = ipv4Mask;
}
#endregion
private static void CheckMaskBytes(byte[] bytes)
{
if (!bytes.RepresentsValidNetMask())
throw new ArgumentException("The passed mask do not represent a valid net mask.");
}
/// <summary>Gets the bits of the net mask instance as an BitArray object instance.</summary>
/// <returns>The bits of the net mask instance as an BitArray object instance.</returns>
public byte[] GetMaskBytes() => new[] { _maskB0, _maskB1, _maskB2, _maskB3 };
private static int GetCidr(uint mask)
{
Debug.Assert(sizeof(uint) == MaskLength);
return mask.CountOnesFromLeft();
}
/// <summary>Extends the current <see cref="T:System.Net.Topology.NetMask"/> instance by a given value (CIDR-wise).</summary>
/// <param name="mask">The mask to use as a reference.</param>
/// <param name="value">The value.</param>
/// <remarks>Because <see cref="T:System.Net.Topology.NetMask"/> is a reference type, this method is static. If it were not like this, you could change the value of <see cref="T:System.Net.Topology.NetMask"/>.Empty, for example.</remarks>
public static NetMask Extend(NetMask mask, int value)
{
int currentCidr = mask.Cidr;
if (currentCidr >= MaskLength * 8)
return new NetMask(mask);
if (currentCidr <= 0)
currentCidr = 0;
int newLength = Math.Max(Math.Min(currentCidr + value, 32), 0);
var m = BytesFromCidrValue(newLength);
return new NetMask(m);
}
/// <summary>Abbreviates the current <see cref="T:System.Net.Topology.NetMask"/> instance by a given value (CIDR-wise).</summary>
/// <param name="mask">The mask to use as a reference.</param>
/// <param name="value">The value.</param>
/// <remarks>Because <see cref="T:System.Net.Topology.NetMask"/> is a reference type, this method is static. If it were not like this, you could change the value of <see cref="T:System.Net.Topology.NetMask"/>.Empty, for example.</remarks>
public static NetMask Abbreviate(NetMask mask, int value)
{
int currentCidr = mask.Cidr;
if (currentCidr < 1)
return new NetMask(mask);
if (currentCidr >= MaskLength * 8)
currentCidr = MaskLength * 8;
int newLength = Math.Max(Math.Min(currentCidr - value, 32), 0);
var m = BytesFromCidrValue(newLength);
return new NetMask(m);
}
/// <summary>Returns a value indicating whether the given array of <see cref="T:System.Byte"/> represents a valid net mask.</summary>
/// <returns>True if the given array of <see cref="T:System.Byte"/> represents a valid net mask, otherwise false.</returns>
public static bool GetIsValidNetMask(byte[] mask) => mask.RepresentsValidNetMask();
// TODO: Testing(!)
[Obsolete]
private static byte[] BytesFromCidrValue(int cidr)
{
int target = MaskLength * 8 - cidr;
int mask = 0;
for (int i = 0; i < target; ++i)
{
mask >>= 1;
mask |= unchecked((int)0x80000000);
}
var bytes = BitConverter.GetBytes(~mask);
return new[] {
bytes[0].ReverseBits(),
bytes[1].ReverseBits(),
bytes[2].ReverseBits(),
bytes[3].ReverseBits()
};
}
// TODO: Testing(!)
private static uint GetUIntFromCidrValue(int cidr) => UIntExtensions.CreateWithOnesFromLeft(cidr);
#region Operators
#region Equality
/// <summary>Returns a other indicating whether two instances of <see cref="T:System.Net.Topology.NetMask" /> are equal.</summary>
/// <param name="n1">The first other to compare.</param>
/// <param name="n2">The second other to compare.</param>
/// <returns>true if <paramref name="n1" /> and <paramref name="n2" /> are equal; otherwise, false.</returns>
public static bool operator ==(NetMask n1, NetMask n2)
{
if (ReferenceEquals(n1, n2))
return true;
if (((object)n1 == null) || ((object)n2 == null))
return false;
return n1.Equals(n2);
}
/// <summary>Returns a other indicating whether two instances of <see cref="T:System.Net.Topology.NetMask" /> are not equal.</summary>
/// <param name="n1">The first other to compare. </param>
/// <param name="n2">The second other to compare. </param>
/// <returns>true if <paramref name="n1" /> and <paramref name="n2" /> are not equal; otherwise, false.</returns>
public static bool operator !=(NetMask n1, NetMask n2) => !(n1 == n2); // Problem solved
#endregion
#region And
/// <summary>Bitwise combines a <see cref="T:System.Net.Topology.NetMask" /> instance and an <see cref="T:System.Net.IPAddress"/> the AND operation.</summary>
/// <param name="mask">The net mask.</param>
/// <param name="address">The IPAddress.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static IPAddress operator &(IPAddress address, NetMask mask) => mask & address;
/// <summary>Bitwise combines a <see cref="T:System.Net.Topology.NetMask" /> instance and an <see cref="T:System.Net.IPAddress"/> the AND operation.</summary>
/// <param name="mask">The net mask.</param>
/// <param name="address">The IPAddress.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static IPAddress BitwiseAnd(IPAddress address, NetMask mask) => mask & address;
/// <summary>Bitwise combines a <see cref="T:System.Net.Topology.NetMask" /> instance and an <see cref="T:System.Net.IPAddress"/> the AND operation.</summary>
/// <param name="mask">The net mask.</param>
/// <param name="address">The IPAddress.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static IPAddress operator &(NetMask mask, IPAddress address)
{
var ipBytes = address == null
? new byte[MaskLength]
: address.GetAddressBytes();
var maskBytes = mask == null
? new byte[MaskLength]
: new byte[] { mask._maskB0, mask._maskB1, mask._maskB2, mask._maskB3 };
byte[] combinedBytes = maskBytes.And(ipBytes);
return new IPAddress(combinedBytes);
}
/// <summary>Bitwise combines a <see cref="T:System.Net.Topology.NetMask" /> instance and an <see cref="T:System.Net.IPAddress"/> the AND operation.</summary>
/// <param name="mask">The net mask.</param>
/// <param name="address">The IPAddress.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static IPAddress BitwiseAnd(NetMask mask, IPAddress address) => mask & address;
/// <summary>Bitwise combines the two instances of <see cref="T:System.Net.Topology.NetMask" /> using the AND operation.</summary>
/// <param name="n1">The first other.</param>
/// <param name="n2">The second other.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static NetMask operator &(NetMask n1, NetMask n2) => new NetMask(n1._mask & n2._mask);
/// <summary>Bitwise combines the two instances of <see cref="T:System.Net.Topology.NetMask" /> using the AND operation.</summary>
/// <param name="n1">The first other.</param>
/// <param name="n2">The second other.</param>
/// <returns>The bitwised combination using the AND operation.</returns>
public static NetMask BitwiseAnd(NetMask n1, NetMask n2) => n1 & n2;
#endregion
#region Or
/// <summary>Bitwise combines the two instances of <see cref="T:System.Net.Topology.NetMask" /> using the OR operation.</summary>
/// <param name="n1">The first other.</param>
/// <param name="n2">The second other.</param>
/// <returns>The bitwised combination using the OR operation.</returns>
public static NetMask operator |(NetMask n1, NetMask n2) => new NetMask(n1._mask | n2._mask);
/// <summary>Bitwise combines the two instances of <see cref="T:System.Net.Topology.NetMask" /> using the OR operation.</summary>
/// <param name="n1">The first other.</param>
/// <param name="n2">The second other.</param>
/// <returns>The bitwised combination using the OR operation.</returns>
public static NetMask BitwiseOr(NetMask n1, NetMask n2) => n1 | n2;
#endregion
#endregion
#region Common overrides
/// <summary>Converts the other of this instance to its equivalent string representation.</summary>
/// <returns>A string that represents the other of this instance.</returns>
/// <filterpriority>1</filterpriority>
public override string ToString()
{
var sb = new StringBuilder(4 * 3 + 3 * 3 + 32 + 3 + 3); // 255.255.255.255 (11111111111111111111111111111111)
var arr = new byte[] { _maskB0, _maskB1, _maskB2, _maskB3 };
var asString = arr.ToBinaryString('.');
sb.Append(arr[0]).Append('.').Append(arr[1]).Append('.').Append(arr[2]).Append('.').Append(arr[3]).Append(" (");
sb.Append(asString).Append(')');
return sb.ToString();
}
/// <summary>Returns a value indicating whether this instance and a specified <see cref="T:System.Object" /> represent the same type and other.</summary>
/// <returns>true if <paramref name="obj" /> is a <see cref="T:System.Net.Topology.NetMask" /> and equal to this instance; otherwise, false.</returns>
/// <param name="obj">The object to compare with this instance. </param>
/// <filterpriority>2</filterpriority>
public override bool Equals(object obj) => obj != null && obj is NetMask && Equals((NetMask)obj);
/// <summary>Returns a value indicating whether this instance and a specified <see cref="T:System.Net.Topology.NetMask" /> object represent the same other.</summary>
/// <returns>true if <paramref name="other" /> is equal to this instance; otherwise, false.</returns>
/// <param name="other">An object to compare to this instance.</param>
/// <filterpriority>2</filterpriority>
public bool Equals(NetMask other) => other._mask == _mask;
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>A 32-bit signed integer hash code.</returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode() => unchecked((int)_mask);
#endregion
}
}
+36
View File
@@ -0,0 +1,36 @@
namespace System.Net.Topology
{
/// <summary>Provides extension methods for the <see cref="T:System.Net.Topology.NetMask"/>.</summary>
public static class NetMaskExtensions
{
/// <summary>Gets the number of siblings an <see cref="T:System.Net.IPAddress"/> can have in a given network. Compliant to RFC 950 (2^n-2).</summary>
/// <param name="mask">The net mask of the network</param>
/// <returns>The number of siblings an <see cref="T:System.Net.IPAddress"/> can have in the given network.</returns>
public static int GetSiblingCount(this NetMask mask) => GetSiblingCount(mask, SiblingOptions.ExcludeUnusable);
/// <summary>Gets the number of siblings an <see cref="T:System.Net.IPAddress"/> can have in a given network.</summary>
/// <param name="mask">The net mask of the network</param>
/// <param name="options">Options which addresses to include an which not</param>
/// <returns>The number of siblings an <see cref="T:System.Net.IPAddress"/> can have in the given network.</returns>
public static int GetSiblingCount(this NetMask mask, SiblingOptions options)
{
if (mask == null)
throw new ArgumentNullException(nameof(mask));
bool includeSelf = BitHelper.IsOptionSet(options, SiblingOptions.IncludeSelf);
bool includeBroadcast = BitHelper.IsOptionSet(options, SiblingOptions.IncludeBroadcast);
bool includeNetworkIdentifier = BitHelper.IsOptionSet(options, SiblingOptions.IncludeNetworkIdentifier);
var hostPartBits = mask.GetMaskBytes().CountFromRight(false);
var total = 1 << hostPartBits;
total -= includeSelf ? 0 : 1;
total -= includeBroadcast ? 0 : 1;
total -= includeNetworkIdentifier ? 0 : 1;
// TODO: Testing
return total;
}
}
}
+27
View File
@@ -0,0 +1,27 @@
namespace System.Net.Topology
{
/// <summary>Provides options for doing network sibling calculations using a net mask.</summary>
[Flags]
public enum SiblingOptions
{
/// <summary>Do not include the broadcast or net address neither the addess passed to the method.</summary>
ExcludeAll = 0,
/// <summary>Include the addess passed to the method.</summary>
IncludeSelf = 1,
/// <summary>Include the addess passed to the method. Compliant to RFC 950 (2^n-2).</summary>
ExcludeUnusable = IncludeSelf,
/// <summary>Include the broadcast address.</summary>
IncludeBroadcast = 2,
/// <summary>Include the net address.</summary>
IncludeNetworkIdentifier = 4,
/// <summary>Include all addresses possible. RFC 1878 (2^n).</summary>
IncludeAll = IncludeSelf | IncludeBroadcast | IncludeNetworkIdentifier
}
internal static class BitHelper
{
[Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] // as if NGen will ever compile this assembly ;)
[Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
internal static bool IsOptionSet(SiblingOptions value, SiblingOptions testValue) => (value & testValue) == testValue;
}
}
+39
View File
@@ -0,0 +1,39 @@
namespace System.Net.Topology
{
internal static class StringExtensions
{
// TODO: The following method uses unsafe stuff. We don't want to pass the /unsafe flag to the compiler, so we use a simple, safe method instead.
// As soon as .NET Standard with Span<T> and the new String.Create is available, we can make this faster and safe.
/*
internal static unsafe string Reverse(this string input)
{
int len = input.Length;
// Why allocate a char[] array on the heap when you won't use it
// outside of this method? Use the stack.
char* reversed = stackalloc char[len];
// Avoid bounds-checking performance penalties.
fixed (char* str = input)
{
int i = 0;
int j = i + len - 1;
while (i < len)
reversed[i++] = str[j--];
}
// Need to use this overload for the System.String constructor
// as providing just the char* pointer could result in garbage
// at the end of the string (no guarantee of null terminator).
return new string(reversed, 0, len);
}
*/
internal static string Reverse(this string input)
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
}
+41
View File
@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace System.Net.Topology
{
internal static class UIntExtensions
{
// TODO: Tests
public static int CountOnesFromLeft(this uint value)
{
var occurences = 0;
for (uint i = 1u << 31; i >= 0; i >>= 1)
{
if ((i & value) != 0)
++occurences;
else
break;
}
return occurences;
}
// TODO: Tests
public static uint CreateWithOnesFromLeft(int count)
{
if (count > 32)
throw new ArgumentException("Cannot set more than 32 bits to one!");
var value = 0u;
var mask = 1u << 31;
for (int i = 0; i < count; ++i)
{
value |= mask;
mask >>= 1;
}
return value;
}
}
}
+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Authors>Niklas Mollenhauer</Authors>
<PackageVersion>2.0.2</PackageVersion>
<Description>Library for Sending magic packets and performing IP address operations.</Description>
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/nikeee/wake-on-lan</PackageProjectUrl>
<RepositoryUrl>https://github.com/nikeee/wake-on-lan.git</RepositoryUrl>
<PackageTags>WakeOnLan, magic packets</PackageTags>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<RootNamespace>System.Net</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.6.0" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
</Project>