mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 16:42:21 -05:00
fix/add various LG/NVIDIA issues
This commit is contained in:
1 parent
977060e927
commit
b2e7c529a0
17 files changed
+752
-147
No files matched your search
@@ -64,7 +64,7 @@ namespace ColorControl
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
public override bool HasDisplaysAttached()
|
||||
public override bool HasDisplaysAttached(bool reinitialize = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<PublisherName>Maassoft</PublisherName>
|
||||
<Company>Maassoft</Company>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>5.3.0.0</ApplicationVersion>
|
||||
<Version>5.3.0.0</Version>
|
||||
<ApplicationVersion>5.4.0.0</ApplicationVersion>
|
||||
<Version>5.4.0.0</Version>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>false</BootstrapperEnabled>
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace ColorControl
|
||||
|
||||
public GraphicsService(string dataPath, string presetsBaseFilename) : base(dataPath, presetsBaseFilename)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~GraphicsService()
|
||||
@@ -23,7 +22,7 @@ namespace ColorControl
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
public abstract bool HasDisplaysAttached();
|
||||
public abstract bool HasDisplaysAttached(bool reinitialize = false);
|
||||
|
||||
protected void ToggleHDR(int delay = 1000)
|
||||
{
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace ColorControl
|
||||
public bool PowerOffOnShutdown { get; set; }
|
||||
public bool PowerOffOnStandby { get; set; }
|
||||
public bool PowerSwitchOnScreenSaver { get; set; }
|
||||
public bool PowerOnAfterManualPowerOff { get; set; }
|
||||
public bool TriggersEnabled { get; set; }
|
||||
|
||||
private List<string> _actionsForGameBar;
|
||||
|
||||
@@ -124,6 +126,7 @@ namespace ColorControl
|
||||
MacAddress = macAddress;
|
||||
IsCustom = isCustom;
|
||||
IsDummy = isDummy;
|
||||
TriggersEnabled = true;
|
||||
|
||||
AddInvokableAction("WOL", new Func<Dictionary<string, object>, bool>(WakeAction));
|
||||
AddGenericPictureAction("backlight", minValue: 0, maxValue: 100);
|
||||
@@ -131,6 +134,7 @@ namespace ColorControl
|
||||
AddGenericPictureAction("contrast", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("color", minValue: 0, maxValue: 100);
|
||||
AddGenericPictureAction("pictureMode", typeof(PictureMode), title: "Picture Mode");
|
||||
//AddGenericPictureAction("dynamicRange", typeof(DynamicRange), category: "dimensionInfo", title: "Dynamic Range");
|
||||
AddGenericPictureAction("colorGamut", typeof(ColorGamut), title: "Color Gamut");
|
||||
AddGenericPictureAction("dynamicContrast", typeof(OffToHigh), title: "Dynamic Contrast");
|
||||
AddGenericPictureAction("gamma", typeof(GammaExp));
|
||||
@@ -159,6 +163,7 @@ namespace ColorControl
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi3", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("hdmiPcMode_hdmi4", typeof(FalseToTrue), category: "other");
|
||||
AddGenericPictureAction("adjustingLuminance", minValue: -50, maxValue: 50);
|
||||
//AddGenericPictureAction("wb20PointsGammaValue", minValue: -50, maxValue: 50);
|
||||
AddInvokableAction("turnScreenOff", new Func<Dictionary<string, object>, bool>(TurnScreenOffAction));
|
||||
AddInvokableAction("turnScreenOn", new Func<Dictionary<string, object>, bool>(TurnScreenOnAction));
|
||||
|
||||
@@ -170,6 +175,11 @@ namespace ColorControl
|
||||
AddSetDeviceConfigAction("HDMI_2_icon", typeof(HdmiIcon), "HDMI 2 icon");
|
||||
AddSetDeviceConfigAction("HDMI_3_icon", typeof(HdmiIcon), "HDMI 3 icon");
|
||||
AddSetDeviceConfigAction("HDMI_4_icon", typeof(HdmiIcon), "HDMI 4 icon");
|
||||
|
||||
AddGenericPictureAction("soundMode", typeof(SoundMode), category: "sound", title: "Sound Mode");
|
||||
AddGenericPictureAction("soundOutput", typeof(SoundOutput), category: "sound", title: "Sound Output");
|
||||
//await ExecuteRequest("luna://com.webos.settingsservice/setSystemSettings", new { category = "network", settings = new { wolwowlOnOff = "true" } });
|
||||
AddGenericPictureAction("wolwowlOnOff", typeof(FalseToTrue), category: "network", title: "Wake-On-LAN");
|
||||
}
|
||||
|
||||
private void AddInvokableAction(string name, Func<Dictionary<string, object>, bool> function)
|
||||
@@ -272,7 +282,7 @@ namespace ColorControl
|
||||
await _lgTvApi.SubscribePowerState(PowerStateChanged);
|
||||
await _lgTvApi.SubscribePictureSettings(PictureSettingsChanged);
|
||||
|
||||
//await _lgTvApi.SetDeviceConfig("PC");
|
||||
//await _lgTvApi.Reboot();
|
||||
|
||||
//var result = await GetPictureSettings();
|
||||
|
||||
@@ -788,9 +798,9 @@ namespace ColorControl
|
||||
internal async Task<dynamic> GetPictureSettings()
|
||||
{
|
||||
//var keys = new[] { "backlight", "brightness", "contrast", "color", "pictureMode", "colorGamut", "dynamicContrast", "peakBrightness", "smoothGradation", "energySaving", "motionProOLED" };
|
||||
var keys = new[] { "backlight", "brightness", "contrast", "color" };
|
||||
//var keys = new[] { "backlight", "brightness", "contrast", "color" };
|
||||
|
||||
return await _lgTvApi.GetSystemSettings("picture", keys);
|
||||
return await _lgTvApi.GetSystemSettings2("picture");
|
||||
}
|
||||
|
||||
private bool WakeAction(Dictionary<string, object> parameters)
|
||||
|
||||
Generated
+16
-11
@@ -41,9 +41,10 @@ namespace ColorControl
|
||||
//
|
||||
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, 41);
|
||||
this.flowPanel.Location = new System.Drawing.Point(14, 47);
|
||||
this.flowPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(231, 282);
|
||||
this.flowPanel.Size = new System.Drawing.Size(270, 325);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RemoteControlForm_MouseDown);
|
||||
this.flowPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.flowPanel_MouseMove);
|
||||
@@ -56,9 +57,10 @@ namespace ColorControl
|
||||
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(240, 0);
|
||||
this.btnClose.Location = new System.Drawing.Point(280, 0);
|
||||
this.btnClose.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Size = new System.Drawing.Size(14, 23);
|
||||
this.btnClose.Size = new System.Drawing.Size(16, 27);
|
||||
this.btnClose.TabIndex = 1;
|
||||
this.btnClose.TabStop = false;
|
||||
this.btnClose.Text = "x";
|
||||
@@ -69,9 +71,10 @@ namespace ColorControl
|
||||
//
|
||||
this.lblTvName.AutoSize = true;
|
||||
this.lblTvName.ForeColor = System.Drawing.Color.White;
|
||||
this.lblTvName.Location = new System.Drawing.Point(12, 0);
|
||||
this.lblTvName.Location = new System.Drawing.Point(14, 0);
|
||||
this.lblTvName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lblTvName.Name = "lblTvName";
|
||||
this.lblTvName.Size = new System.Drawing.Size(25, 13);
|
||||
this.lblTvName.Size = new System.Drawing.Size(25, 15);
|
||||
this.lblTvName.TabIndex = 0;
|
||||
this.lblTvName.Text = " ";
|
||||
//
|
||||
@@ -84,33 +87,35 @@ namespace ColorControl
|
||||
//
|
||||
this.lblProcessName.AutoSize = true;
|
||||
this.lblProcessName.ForeColor = System.Drawing.Color.White;
|
||||
this.lblProcessName.Location = new System.Drawing.Point(12, 20);
|
||||
this.lblProcessName.Location = new System.Drawing.Point(14, 23);
|
||||
this.lblProcessName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lblProcessName.Name = "lblProcessName";
|
||||
this.lblProcessName.Size = new System.Drawing.Size(25, 13);
|
||||
this.lblProcessName.Size = new System.Drawing.Size(25, 15);
|
||||
this.lblProcessName.TabIndex = 2;
|
||||
this.lblProcessName.Text = " ";
|
||||
//
|
||||
// LgGameBar
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Black;
|
||||
this.ClientSize = new System.Drawing.Size(255, 335);
|
||||
this.ClientSize = new System.Drawing.Size(298, 387);
|
||||
this.Controls.Add(this.lblProcessName);
|
||||
this.Controls.Add(this.lblTvName);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.flowPanel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.KeyPreview = true;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "LgGameBar";
|
||||
this.Opacity = 0.7D;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Text = "RemoteControlForm";
|
||||
this.TopMost = true;
|
||||
this.Activated += new System.EventHandler(this.LgGameBar_Activated);
|
||||
this.Deactivate += new System.EventHandler(this.LgGameBar_Deactivate);
|
||||
this.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);
|
||||
|
||||
@@ -178,6 +178,8 @@ namespace ColorControl
|
||||
|
||||
private void RemoteControlForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
Left = _lgService.Config.GameBarLeft;
|
||||
Top = _lgService.Config.GameBarTop;
|
||||
}
|
||||
|
||||
private void ButtonClick(object sender, EventArgs e)
|
||||
@@ -242,22 +244,6 @@ namespace ColorControl
|
||||
ResetAutoHide();
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseEnter(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void RemoteControlForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void HandleMouse(MouseEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void RemoteControlForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
GenerateComponents();
|
||||
@@ -273,22 +259,17 @@ namespace ColorControl
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
HandleMouse(e);
|
||||
}
|
||||
|
||||
private void pnlMouse_MouseWheel(object sender, MouseEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void LgGameBar_Deactivate(object sender, EventArgs e)
|
||||
{
|
||||
_lgService.Config.GameBarLeft = Left;
|
||||
_lgService.Config.GameBarTop = Top;
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void tmrHide_Tick(object sender, EventArgs e)
|
||||
{
|
||||
_lgService.Config.GameBarLeft = Left;
|
||||
_lgService.Config.GameBarTop = Top;
|
||||
Hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,64 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
@@ -433,7 +433,7 @@ namespace ColorControl
|
||||
state == PowerOnOffState.ScreenSaver && d.PowerSwitchOnScreenSaver);
|
||||
foreach (var device in wakeDevices)
|
||||
{
|
||||
if (device.PoweredOffBy == LgDevice.PowerOffSource.Manually)
|
||||
if (!device.PowerOnAfterManualPowerOff && device.PoweredOffBy == LgDevice.PowerOffSource.Manually)
|
||||
{
|
||||
Logger.Debug($"[{device.Name}]: device was manually powered off by user, not powering on");
|
||||
continue;
|
||||
@@ -557,8 +557,8 @@ namespace ColorControl
|
||||
|
||||
try
|
||||
{
|
||||
var applicableDevices = Devices.Where(d => d.PowerSwitchOnScreenSaver || _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
|
||||
((p.DeviceMacAddress == null && d == SelectedDevice) || p.DeviceMacAddress == d.MacAddress)));
|
||||
var applicableDevices = Devices.Where(d => d.PowerSwitchOnScreenSaver || d.TriggersEnabled && _presets.Any(p => p.Triggers.Any(t => t.Trigger != PresetTriggerType.None) &&
|
||||
((string.IsNullOrEmpty(p.DeviceMacAddress) && d == SelectedDevice) || p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
|
||||
|
||||
if (!applicableDevices.Any())
|
||||
{
|
||||
@@ -623,7 +623,7 @@ namespace ColorControl
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
MonitorContext.Devices = connectedDevices;
|
||||
MonitorContext.Devices = connectedDevices.Where(d => d.TriggersEnabled).ToList();
|
||||
MonitorContext.RunningProcesses = processes;
|
||||
//if (lastProcesses != null)
|
||||
//{
|
||||
@@ -646,7 +646,7 @@ namespace ColorControl
|
||||
|
||||
if (process != null)
|
||||
{
|
||||
var parent = process.Parent();
|
||||
var parent = process.Parent(processes);
|
||||
|
||||
if (parent?.ProcessName.Contains("winlogon") ?? false)
|
||||
{
|
||||
@@ -705,7 +705,11 @@ namespace ColorControl
|
||||
|
||||
private async Task ExecuteProcessPresets(ProcessMonitorContext context)
|
||||
{
|
||||
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == PresetTriggerType.ProcessSwitch));
|
||||
var selectedDevice = SelectedDevice;
|
||||
|
||||
var presets = _presets.Where(p => p.Triggers.Any(t => t.Trigger == PresetTriggerType.ProcessSwitch) &&
|
||||
((string.IsNullOrEmpty(p.DeviceMacAddress) && selectedDevice?.TriggersEnabled == true)
|
||||
|| Devices.Any(d => d.TriggersEnabled && p.DeviceMacAddress.Equals(d.MacAddress, StringComparison.OrdinalIgnoreCase))));
|
||||
if (!presets.Any())
|
||||
{
|
||||
return;
|
||||
@@ -715,7 +719,7 @@ namespace ColorControl
|
||||
|
||||
var (processId, isFullScreen) = Utils.GetForegroundProcessIdAndIfFullScreen();
|
||||
|
||||
if (processId == Process.GetCurrentProcess().Id)
|
||||
if (processId == Environment.ProcessId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -808,7 +812,7 @@ namespace ColorControl
|
||||
|
||||
if (!string.IsNullOrEmpty(device.MacAddress))
|
||||
{
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress)).ToList();
|
||||
var presets = _presets.Where(p => !string.IsNullOrEmpty(p.DeviceMacAddress) && p.DeviceMacAddress.Equals(device.MacAddress, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
presets.ForEach(preset =>
|
||||
{
|
||||
preset.DeviceMacAddress = null;
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace ColorControl
|
||||
public bool ShowRemoteControl { get; set; }
|
||||
public bool ShowAdvancedActions { get; set; }
|
||||
public string GameBarShortcut { get; set; }
|
||||
public int GameBarTop { get; set; }
|
||||
public int GameBarLeft { get; set; }
|
||||
|
||||
public LgServiceConfig()
|
||||
{
|
||||
@@ -20,6 +22,8 @@ namespace ColorControl
|
||||
DeviceSearchKey = "[LG]";
|
||||
PowerOnRetries = 10;
|
||||
Devices = new List<LgDevice>();
|
||||
GameBarTop = 100;
|
||||
GameBarLeft = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+26
-11
@@ -104,6 +104,7 @@
|
||||
this.lvAmdPresets = new System.Windows.Forms.ListView();
|
||||
this.tabLG = new System.Windows.Forms.TabPage();
|
||||
this.scLgController = new System.Windows.Forms.SplitContainer();
|
||||
this.btnLgDeviceOptionsHelp = new System.Windows.Forms.Button();
|
||||
this.lblGameBarShortcut = new System.Windows.Forms.Label();
|
||||
this.edtLgGameBarShortcut = new System.Windows.Forms.TextBox();
|
||||
this.btnLgGameBar = new System.Windows.Forms.Button();
|
||||
@@ -981,6 +982,7 @@
|
||||
//
|
||||
// scLgController.Panel1
|
||||
//
|
||||
this.scLgController.Panel1.Controls.Add(this.btnLgDeviceOptionsHelp);
|
||||
this.scLgController.Panel1.Controls.Add(this.lblGameBarShortcut);
|
||||
this.scLgController.Panel1.Controls.Add(this.edtLgGameBarShortcut);
|
||||
this.scLgController.Panel1.Controls.Add(this.btnLgGameBar);
|
||||
@@ -1030,6 +1032,18 @@
|
||||
this.scLgController.SplitterWidth = 5;
|
||||
this.scLgController.TabIndex = 43;
|
||||
//
|
||||
// btnLgDeviceOptionsHelp
|
||||
//
|
||||
this.btnLgDeviceOptionsHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnLgDeviceOptionsHelp.Location = new System.Drawing.Point(867, 70);
|
||||
this.btnLgDeviceOptionsHelp.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.btnLgDeviceOptionsHelp.Name = "btnLgDeviceOptionsHelp";
|
||||
this.btnLgDeviceOptionsHelp.Size = new System.Drawing.Size(22, 27);
|
||||
this.btnLgDeviceOptionsHelp.TabIndex = 58;
|
||||
this.btnLgDeviceOptionsHelp.Text = "?";
|
||||
this.btnLgDeviceOptionsHelp.UseVisualStyleBackColor = true;
|
||||
this.btnLgDeviceOptionsHelp.Click += new System.EventHandler(this.btnLgDeviceOptionsHelp_Click);
|
||||
//
|
||||
// lblGameBarShortcut
|
||||
//
|
||||
this.lblGameBarShortcut.AutoSize = true;
|
||||
@@ -1278,11 +1292,11 @@
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.lvLgPresets.FullRowSelect = true;
|
||||
this.lvLgPresets.Location = new System.Drawing.Point(6, 168);
|
||||
this.lvLgPresets.Location = new System.Drawing.Point(6, 152);
|
||||
this.lvLgPresets.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.lvLgPresets.MultiSelect = false;
|
||||
this.lvLgPresets.Name = "lvLgPresets";
|
||||
this.lvLgPresets.Size = new System.Drawing.Size(883, 130);
|
||||
this.lvLgPresets.Size = new System.Drawing.Size(883, 146);
|
||||
this.lvLgPresets.TabIndex = 8;
|
||||
this.lvLgPresets.UseCompatibleStateImageBehavior = false;
|
||||
this.lvLgPresets.View = System.Windows.Forms.View.Details;
|
||||
@@ -1575,21 +1589,21 @@
|
||||
this.clbLgPower.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.clbLgPower.CheckOnClick = true;
|
||||
this.clbLgPower.ColumnWidth = 380;
|
||||
this.clbLgPower.FormattingEnabled = true;
|
||||
this.clbLgPower.Items.AddRange(new object[] {
|
||||
"Automatically power on after startup. Requires \"Automatically start after login\" " +
|
||||
"- see Options.",
|
||||
"Automatically power on after resume from standby. May need some retries for wakin" +
|
||||
"g TV, see Options.",
|
||||
"Automatically power off on shutdown. Because this app cannot detect a restart, re" +
|
||||
"starting could also trigger this. Hold down Ctrl on restart to prevent power off" +
|
||||
".",
|
||||
"Automatically power on after startup",
|
||||
"Automatically power on after resume from standby",
|
||||
"Automatically power off on shutdown",
|
||||
"Automatically power off on standby",
|
||||
"Automatically power off on screensaver and power on when screensaver deactivates"});
|
||||
"Automatically power off/on when screensaver activates/deactivates",
|
||||
"Power on even after manual power off",
|
||||
"Allow triggers to be fired for this device"});
|
||||
this.clbLgPower.Location = new System.Drawing.Point(6, 70);
|
||||
this.clbLgPower.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.clbLgPower.MultiColumn = true;
|
||||
this.clbLgPower.Name = "clbLgPower";
|
||||
this.clbLgPower.Size = new System.Drawing.Size(883, 76);
|
||||
this.clbLgPower.Size = new System.Drawing.Size(856, 76);
|
||||
this.clbLgPower.TabIndex = 34;
|
||||
this.clbLgPower.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.clbLgPower_ItemCheck);
|
||||
//
|
||||
@@ -2353,6 +2367,7 @@
|
||||
private System.Windows.Forms.Label lblGameBarShortcut;
|
||||
private System.Windows.Forms.TextBox edtLgGameBarShortcut;
|
||||
private System.Windows.Forms.CheckBox chkGdiScaling;
|
||||
private System.Windows.Forms.Button btnLgDeviceOptionsHelp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using LgTv;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NonInvasiveKeyboardHookLibrary;
|
||||
using NStandard;
|
||||
using NvAPIWrapper.Display;
|
||||
using NvAPIWrapper.Native.Display;
|
||||
@@ -59,6 +60,8 @@ namespace ColorControl
|
||||
|
||||
private LgGameBar _gameBarForm;
|
||||
|
||||
private KeyboardHookManager _keyboardHookManager;
|
||||
|
||||
public MainForm(AppContext appContext)
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -72,7 +75,7 @@ namespace ColorControl
|
||||
MessageForms.MainForm = this;
|
||||
|
||||
_nvTrayMenu = new ToolStripMenuItem("NVIDIA presets");
|
||||
_amdTrayMenu = new ToolStripMenuItem("NVIDIA presets");
|
||||
_amdTrayMenu = new ToolStripMenuItem("AMD presets");
|
||||
_lgTrayMenu = new ToolStripMenuItem("LG presets");
|
||||
_trayIcon = new NotifyIcon()
|
||||
{
|
||||
@@ -205,6 +208,14 @@ namespace ColorControl
|
||||
{
|
||||
Utils.RegisterShortcut(Handle, SHORTCUTID_GAMEBAR, _lgService.Config.GameBarShortcut);
|
||||
|
||||
// New shortcut manager, not working yet
|
||||
//if (_keyboardHookManager == null)
|
||||
//{
|
||||
// _keyboardHookManager = new KeyboardHookManager();
|
||||
// _keyboardHookManager.Start();
|
||||
//}
|
||||
//_keyboardHookManager.RegisterHotkey(NonInvasiveKeyboardHookLibrary.ModifierKeys.Control | NonInvasiveKeyboardHookLibrary.ModifierKeys.Alt, 0x47, new Action(Invoke));
|
||||
|
||||
edtLgGameBarShortcut.Text = _lgService.Config.GameBarShortcut;
|
||||
}
|
||||
}
|
||||
@@ -214,6 +225,11 @@ namespace ColorControl
|
||||
}
|
||||
}
|
||||
|
||||
private void Invoke()
|
||||
{
|
||||
BeginInvoke(new Action(ToggleGameBar));
|
||||
}
|
||||
|
||||
private void AfterLgServiceRefreshDevices()
|
||||
{
|
||||
FillLgDevices();
|
||||
@@ -1464,6 +1480,8 @@ namespace ColorControl
|
||||
clbLgPower.SetItemChecked(2, device?.PowerOffOnShutdown ?? false);
|
||||
clbLgPower.SetItemChecked(3, device?.PowerOffOnStandby ?? false);
|
||||
clbLgPower.SetItemChecked(4, device?.PowerSwitchOnScreenSaver ?? false);
|
||||
clbLgPower.SetItemChecked(5, device?.PowerOnAfterManualPowerOff ?? false);
|
||||
clbLgPower.SetItemChecked(6, device?.TriggersEnabled ?? true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1939,7 +1957,8 @@ namespace ColorControl
|
||||
|
||||
Connection > Mobile TV On > Turn on via Wi-Fi (Networked Standby Mode)
|
||||
|
||||
See Options to test this functionality."
|
||||
See Options to test this functionality.
|
||||
You can also activate this option by using the Expert-button and selecting Wake-On-LAN > Enabled."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1950,6 +1969,8 @@ See Options to test this functionality."
|
||||
device.PowerOffOnShutdown = clbLgPower.GetItemChecked(2);
|
||||
device.PowerOffOnStandby = clbLgPower.GetItemChecked(3);
|
||||
device.PowerSwitchOnScreenSaver = clbLgPower.GetItemChecked(4);
|
||||
device.PowerOnAfterManualPowerOff = clbLgPower.GetItemChecked(5);
|
||||
device.TriggersEnabled = clbLgPower.GetItemChecked(6);
|
||||
|
||||
_lgService.InstallEventHandlers();
|
||||
}));
|
||||
@@ -1978,8 +1999,6 @@ See Options to test this functionality."
|
||||
|
||||
private void btnLGTestPower_Click(object sender, EventArgs e)
|
||||
{
|
||||
//_lgService.WakeSelectedDevice();
|
||||
//return;
|
||||
var text =
|
||||
@"The TV will now power off. Please wait for the TV to be powered off completely (relay click) and press ENTER to wake it again.
|
||||
For waking up to work, you need to activate the following setting on the TV:
|
||||
@@ -1987,6 +2006,8 @@ For waking up to work, you need to activate the following setting on the TV:
|
||||
Connection > Mobile TV On > Turn on via Wi-Fi
|
||||
|
||||
It will also work over a wired connection.
|
||||
You can also activate this option by using the Expert-button and selecting Wake-On-LAN > Enabled.
|
||||
|
||||
Do you want to continue?";
|
||||
|
||||
if (MessageForms.QuestionYesNo(text) == DialogResult.Yes)
|
||||
@@ -2983,6 +3004,11 @@ The InStart and Software Update items are now visible under the Expert-button."
|
||||
{
|
||||
if (_gameBarForm == null || _gameBarForm.IsDisposed)
|
||||
{
|
||||
if (_lgService?.SelectedDevice == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_gameBarForm = new LgGameBar(_lgService);
|
||||
}
|
||||
|
||||
@@ -3054,5 +3080,15 @@ The InStart and Software Update items are now visible under the Expert-button."
|
||||
|
||||
ApplyDitheringOptions();
|
||||
}
|
||||
|
||||
private void btnLgDeviceOptionsHelp_Click(object sender, EventArgs e)
|
||||
{
|
||||
MessageForms.InfoOk(
|
||||
@"Notes:
|
||||
- power on after startup requires ""Automatically start after login"" - see Options
|
||||
- power on after resume from standby may need some retries for waking TV - see Options
|
||||
- power off on shutdown: because this app cannot detect a restart, restarting could also trigger this. Hold down Ctrl on restart to prevent power off.
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// A struct to represent a keybind (key + modifiers)
|
||||
/// When the keybind struct is compared to another keybind struct, the equality is based on the
|
||||
/// modifiers and the virtual key code.
|
||||
/// </summary>
|
||||
internal class KeybindStruct : IEquatable<KeybindStruct>
|
||||
{
|
||||
public readonly int VirtualKeyCode;
|
||||
public readonly List<ModifierKeys> Modifiers;
|
||||
public readonly Guid? Identifier;
|
||||
|
||||
public KeybindStruct(IEnumerable<ModifierKeys> modifiers, int virtualKeyCode, Guid? identifier = null)
|
||||
{
|
||||
this.VirtualKeyCode = virtualKeyCode;
|
||||
this.Modifiers = new List<ModifierKeys>(modifiers);
|
||||
this.Identifier = identifier;
|
||||
}
|
||||
|
||||
public bool Equals(KeybindStruct other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (this.VirtualKeyCode != other.VirtualKeyCode)
|
||||
return false;
|
||||
|
||||
if (this.Modifiers.Count != other.Modifiers.Count)
|
||||
return false;
|
||||
|
||||
foreach (var modifier in this.Modifiers)
|
||||
{
|
||||
if (!other.Modifiers.Contains(modifier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
|
||||
return Equals((KeybindStruct) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 13;
|
||||
hash = (hash * 7) + VirtualKeyCode.GetHashCode();
|
||||
|
||||
// Sum the modifiers separately, so that their order does not affect the final hash
|
||||
var modifiersHashSum = 0;
|
||||
foreach (var modifier in this.Modifiers)
|
||||
{
|
||||
modifiersHashSum += modifier.GetHashCode();
|
||||
}
|
||||
|
||||
hash = (hash * 7) + modifiersHashSum;
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
internal struct KeyboardParams
|
||||
{
|
||||
public IntPtr wParam;
|
||||
public int vkCode;
|
||||
|
||||
public KeyboardParams(IntPtr wParam, int vkCode)
|
||||
{
|
||||
this.wParam = wParam;
|
||||
this.vkCode = vkCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hotkey manager that uses a low-level global keyboard hook, but eventually only fires events for
|
||||
/// pre-registered hotkeys, i.e. not invading a user's privacy.
|
||||
/// </summary>
|
||||
public class KeyboardHookManager
|
||||
{
|
||||
#region Private Attributes
|
||||
/// <summary>
|
||||
/// Keeps track of all registered hotkeys
|
||||
/// </summary>
|
||||
private readonly Dictionary<KeybindStruct, Action> _registeredCallbacks;
|
||||
/// <summary>
|
||||
/// Keeps track of modifier keys that are held down
|
||||
/// </summary>
|
||||
private readonly HashSet<ModifierKeys> _downModifierKeys;
|
||||
/// <summary>
|
||||
/// Keeps track of all keys that are held down to prevent firing callbacks
|
||||
/// more than once for a single keypress
|
||||
/// </summary>
|
||||
private readonly HashSet<int> _downKeys;
|
||||
private readonly object _modifiersLock = new object();
|
||||
private LowLevelKeyboardProc _hook;
|
||||
private bool _isStarted;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Instantiates an empty keyboard hook manager.
|
||||
/// It is best practice to keep a single instance per process.
|
||||
/// Start() must be called to start the low-level keyboard hook manager
|
||||
/// </summary>
|
||||
public KeyboardHookManager()
|
||||
{
|
||||
this._registeredCallbacks = new Dictionary<KeybindStruct, Action>();
|
||||
this._downModifierKeys = new HashSet<ModifierKeys>();
|
||||
this._downKeys = new HashSet<int>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// Starts the low-level keyboard hook.
|
||||
/// Hotkeys can be registered regardless of the low-level keyboard hook's state, but their callbacks
|
||||
/// will only ever be invoked if the low-level keyboard hook is running and intercepting keys.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (this._isStarted) return;
|
||||
|
||||
this._hook = this.HookCallback;
|
||||
_hookId = SetHook(this._hook);
|
||||
this._isStarted = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the low-level keyboard hook (without unregistering the existing hotkeys)
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (this._isStarted)
|
||||
{
|
||||
UnhookWindowsHookEx(_hookId);
|
||||
this._isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a hotkey.
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the hotkey</param>
|
||||
/// <param name="action">The callback action to invoke when this hotkey is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key is already mapped to a callback</exception>
|
||||
/// /// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(int virtualKeyCode, Action action)
|
||||
{
|
||||
return this.RegisterHotkey(new ModifierKeys[0], virtualKeyCode, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new key combination with one or more modifiers
|
||||
/// </summary>
|
||||
/// <param name="modifiers">Modifiers that must be held while hitting the key. Multiple modifiers can be provided using the flags bitwise OR operation</param>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the standard key</param>
|
||||
/// <param name="action">The callback action to invoke when this combination is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key combination is already mapped to a callback</exception>
|
||||
/// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(ModifierKeys modifiers, int virtualKeyCode, Action action)
|
||||
{
|
||||
var allModifiers = Enum.GetValues(typeof(ModifierKeys)).Cast<ModifierKeys>().ToArray();
|
||||
|
||||
// Get the modifiers that were chained with bitwise OR operation as an array of modifiers
|
||||
var selectedModifiers = allModifiers.Where(modifier => modifiers.HasFlag(modifier)).ToArray();
|
||||
|
||||
return RegisterHotkey(selectedModifiers, virtualKeyCode, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new key combination.
|
||||
/// </summary>
|
||||
/// <param name="modifiers">Modifiers that must be held while hitting the key</param>
|
||||
/// <param name="virtualKeyCode">The virtual key code of the standard key</param>
|
||||
/// <param name="action">The callback action to invoke when this combination is pressed</param>
|
||||
/// <exception cref="HotkeyAlreadyRegisteredException">Thrown when the given key combination is already mapped to a callback</exception>
|
||||
/// <returns>Unique identifier of this hotkey, which can be used to remove it later</returns>
|
||||
public Guid RegisterHotkey(ModifierKeys[] modifiers, int virtualKeyCode, Action action)
|
||||
{
|
||||
var keybindIdentity = Guid.NewGuid();
|
||||
var keybind = new KeybindStruct(modifiers, virtualKeyCode, keybindIdentity);
|
||||
if (this._registeredCallbacks.ContainsKey(keybind))
|
||||
{
|
||||
throw new HotkeyAlreadyRegisteredException();
|
||||
}
|
||||
|
||||
this._registeredCallbacks[keybind] = action;
|
||||
return keybindIdentity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters all hotkeys (the low-level keyboard hook continues running)
|
||||
/// </summary>
|
||||
public void UnregisterAll()
|
||||
{
|
||||
this._registeredCallbacks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific single-key hotkey
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode">Virtual key code of the unregistered key</param>
|
||||
/// <exception cref="HotkeyNotRegisteredException">Thrown when the given key combination is not registered</exception>
|
||||
public void UnregisterHotkey(int virtualKeyCode)
|
||||
{
|
||||
this.UnregisterHotkey(new ModifierKeys[0], virtualKeyCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific key combination
|
||||
/// </summary>
|
||||
/// <param name="modifiers">The modifiers of the combination</param>
|
||||
/// <param name="virtualKeyCode">The key of the combination</param>
|
||||
/// <exception cref="HotkeyNotRegisteredException">Thrown when the given key combination is not registered</exception>
|
||||
public void UnregisterHotkey(ModifierKeys[] modifiers, int virtualKeyCode)
|
||||
{
|
||||
var keybind = new KeybindStruct(modifiers, virtualKeyCode);
|
||||
|
||||
if (!this._registeredCallbacks.Remove(keybind))
|
||||
{
|
||||
throw new HotkeyNotRegisteredException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a specific key by its unique identifier
|
||||
/// </summary>
|
||||
/// <param name="keybindIdentity">Keybind GUID, as returned by RegisterHotkey</param>
|
||||
public void UnregisterHotkey(Guid keybindIdentity)
|
||||
{
|
||||
var keybindToRemove = this._registeredCallbacks.Keys.FirstOrDefault(keybind =>
|
||||
keybind.Identifier.HasValue && keybind.Identifier.Value.Equals(keybindIdentity));
|
||||
|
||||
if (keybindToRemove == null || !this._registeredCallbacks.Remove(keybindToRemove))
|
||||
{
|
||||
throw new HotkeyNotRegisteredException();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
private void HandleKeyPress(int virtualKeyCode)
|
||||
{
|
||||
var currentKey = new KeybindStruct(this._downModifierKeys, virtualKeyCode);
|
||||
|
||||
if (!this._registeredCallbacks.ContainsKey(currentKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._registeredCallbacks.TryGetValue(currentKey, out var callback))
|
||||
{
|
||||
callback.Invoke();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Low level keyboard hook
|
||||
// Source: https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/
|
||||
private const int WH_KEYBOARD_LL = 13;
|
||||
private const int WM_KEYDOWN = 0x0100;
|
||||
private const int WM_KEYUP = 0x0101;
|
||||
private const int WM_SYSKEYDOWN = 0x0104;
|
||||
private const int WM_SYSKEYUP = 0x0105;
|
||||
|
||||
private static IntPtr _hookId = IntPtr.Zero;
|
||||
|
||||
private static IntPtr SetHook(LowLevelKeyboardProc proc)
|
||||
{
|
||||
var userLibrary = LoadLibrary("User32");
|
||||
|
||||
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
|
||||
userLibrary, 0);
|
||||
}
|
||||
|
||||
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
var vkCode = Marshal.ReadInt32(lParam);
|
||||
|
||||
// Debug.WriteLine("Starting");
|
||||
// To prevent slowing keyboard input down, we use handle keyboard inputs in a separate thread
|
||||
ThreadPool.QueueUserWorkItem(this.HandleSingleKeyboardInput, new KeyboardParams(wParam, vkCode));
|
||||
// Debug.WriteLine("Ending");
|
||||
}
|
||||
|
||||
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a keyboard event based on the KeyboardParams it receives
|
||||
/// </summary>
|
||||
/// <param name="keyboardParamsObj">KeyboardParams struct (object type to comply with QueueUserWorkItem)</param>
|
||||
private void HandleSingleKeyboardInput(object keyboardParamsObj)
|
||||
{
|
||||
var keyboardParams = (KeyboardParams)keyboardParamsObj;
|
||||
var wParam = keyboardParams.wParam;
|
||||
var vkCode = keyboardParams.vkCode;
|
||||
|
||||
var modifierKey = ModifierKeysUtilities.GetModifierKeyFromCode(vkCode);
|
||||
|
||||
// If the keyboard event is a KeyDown event (i.e. key pressed)
|
||||
if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
|
||||
{
|
||||
// In this case, we only care about modifier keys
|
||||
if (modifierKey != null)
|
||||
{
|
||||
lock (this._modifiersLock)
|
||||
{
|
||||
this._downModifierKeys.Add(modifierKey.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger callbacks that are registered for this key, but only once per key press
|
||||
if (!this._downKeys.Contains(vkCode))
|
||||
{
|
||||
this.HandleKeyPress(vkCode);
|
||||
this._downKeys.Add(vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
// If the keyboard event is a KeyUp event (i.e. key released)
|
||||
if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
|
||||
{
|
||||
// If the released key is a modifier key, remove it from the HashSet of modifier keys
|
||||
if (modifierKey != null)
|
||||
{
|
||||
lock (this._modifiersLock)
|
||||
{
|
||||
this._downModifierKeys.Remove(modifierKey.Value);
|
||||
}
|
||||
}
|
||||
|
||||
this._downKeys.Remove(vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr SetWindowsHookEx(int idHook,
|
||||
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
|
||||
IntPtr wParam, IntPtr lParam);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the library.
|
||||
/// </summary>
|
||||
/// <param name="lpFileName">Name of the library</param>
|
||||
/// <returns>A handle to the library</returns>
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Exceptions
|
||||
public class NonInvasiveKeyboardHookException : Exception
|
||||
{
|
||||
}
|
||||
|
||||
public class HotkeyAlreadyRegisteredException : NonInvasiveKeyboardHookException
|
||||
{
|
||||
}
|
||||
|
||||
public class HotkeyNotRegisteredException : NonInvasiveKeyboardHookException
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace NonInvasiveKeyboardHookLibrary
|
||||
{
|
||||
[Flags]
|
||||
public enum ModifierKeys
|
||||
{
|
||||
Alt = 1,
|
||||
Control = 2,
|
||||
Shift = 4,
|
||||
WindowsKey = 8,
|
||||
}
|
||||
|
||||
public static class ModifierKeysUtilities
|
||||
{
|
||||
public static ModifierKeys? GetModifierKeyFromCode(int keyCode)
|
||||
{
|
||||
switch (keyCode)
|
||||
{
|
||||
case 0xA0:
|
||||
case 0xA1:
|
||||
case 0x10:
|
||||
return ModifierKeys.Shift;
|
||||
|
||||
case 0xA2:
|
||||
case 0xA3:
|
||||
case 0x11:
|
||||
return ModifierKeys.Control;
|
||||
|
||||
case 0x12:
|
||||
case 0xA4:
|
||||
case 0xA5:
|
||||
return ModifierKeys.Alt;
|
||||
|
||||
case 0x5B:
|
||||
case 0x5C:
|
||||
return ModifierKeys.WindowsKey;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,12 @@ namespace ColorControl
|
||||
|
||||
public Display GetCurrentDisplay()
|
||||
{
|
||||
if (!HasDisplaysAttached())
|
||||
{
|
||||
_currentDisplay = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_currentDisplay == null)
|
||||
{
|
||||
_currentDisplay = Display.GetDisplays()[0];
|
||||
@@ -119,15 +125,28 @@ namespace ColorControl
|
||||
return _currentDisplay;
|
||||
}
|
||||
|
||||
public override bool HasDisplaysAttached()
|
||||
public override bool HasDisplaysAttached(bool reinitialize = false)
|
||||
{
|
||||
if (reinitialize)
|
||||
{
|
||||
try
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Could not reinitialize NVIDIA API: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return DisplayDevice.GetGDIPrimaryDisplayDevice() != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
return reinitialize ? false : HasDisplaysAttached(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +219,11 @@ namespace ColorControl
|
||||
if (preset.applyColorData && (ColorDataDiffers(preset.colorData) || (!newHdrEnabled && preset.applyColorData && preset.colorData.Colorimetry != ColorDataColorimetry.Auto)))
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdrEnabled)
|
||||
{
|
||||
SetHDRState(display, false);
|
||||
@@ -228,6 +252,10 @@ namespace ColorControl
|
||||
if (applyHdr)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var colorData = preset.applyColorData ? preset.colorData : display.DisplayDevice.CurrentColorData;
|
||||
|
||||
@@ -324,7 +352,13 @@ namespace ColorControl
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_SetDitherControl)) as NvAPI_Disp_SetDitherControl;
|
||||
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
|
||||
var gpuHandle = displayDevice.PhysicalGPU.Handle;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
@@ -366,7 +400,14 @@ namespace ColorControl
|
||||
{
|
||||
var delegateValue = Marshal.GetDelegateForFunctionPointer(ptr, typeof(NvAPI_Disp_GetDitherControl)) as NvAPI_Disp_GetDitherControl;
|
||||
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
dither.state = -1;
|
||||
return dither;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
var displayId = displayDevice.DisplayId;
|
||||
|
||||
var result = delegateValue(displayId, ref dither);
|
||||
@@ -387,6 +428,11 @@ namespace ColorControl
|
||||
public bool SetRefreshRate(uint refreshRate)
|
||||
{
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
if (timing.Extra.RefreshRate == refreshRate)
|
||||
@@ -407,6 +453,11 @@ namespace ColorControl
|
||||
}
|
||||
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return new List<uint>();
|
||||
}
|
||||
|
||||
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
|
||||
var timing = display.DisplayDevice.CurrentTiming;
|
||||
|
||||
@@ -415,7 +466,13 @@ namespace ColorControl
|
||||
|
||||
public bool IsHDREnabled()
|
||||
{
|
||||
var displayDevice = GetCurrentDisplay().DisplayDevice;
|
||||
var display = GetCurrentDisplay();
|
||||
if (display == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var displayDevice = display.DisplayDevice;
|
||||
var hdr = displayDevice.HDRColorData;
|
||||
return hdr?.HDRMode == ColorDataHDRMode.UHDA;
|
||||
}
|
||||
|
||||
+33
-14
@@ -605,7 +605,12 @@ namespace ColorControl
|
||||
var itemText = (enumValue as IConvertible).GetDescription() ?? enumValue.ToString();
|
||||
var item = subMenuItem.DropDownItems.Add(itemText);
|
||||
item.Tag = tag;
|
||||
item.AccessibleName = enumValue.ToString();
|
||||
var strValue = enumValue.ToString();
|
||||
if (strValue.EndsWith("_"))
|
||||
{
|
||||
strValue = strValue.Substring(0, strValue.Length - 1);
|
||||
}
|
||||
item.AccessibleName = strValue;
|
||||
item.Click += clickEvent;
|
||||
}
|
||||
}
|
||||
@@ -898,7 +903,7 @@ namespace ColorControl
|
||||
|
||||
public static Process GetProcessByName(string name, bool skipCurrent = true)
|
||||
{
|
||||
var currentProcessId = Process.GetCurrentProcess().Id;
|
||||
var currentProcessId = Environment.ProcessId;
|
||||
foreach (var process in Process.GetProcesses())
|
||||
{
|
||||
if (process.ProcessName.Equals(name) && (!skipCurrent || process.Id != currentProcessId))
|
||||
@@ -1092,34 +1097,48 @@ namespace ColorControl
|
||||
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
private static string FindIndexedProcessName(int pid)
|
||||
private static string FindIndexedProcessName(int pid, string processName, IEnumerable<Process> processes = null)
|
||||
{
|
||||
var processName = Process.GetProcessById(pid).ProcessName;
|
||||
var processesByName = Process.GetProcessesByName(processName);
|
||||
string processIndexdName = null;
|
||||
var processesByName = processes == null ? Process.GetProcessesByName(processName) : processes.Where(p => p.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
string processIndexedName = null;
|
||||
|
||||
for (var index = 0; index < processesByName.Length; index++)
|
||||
{
|
||||
processIndexdName = index == 0 ? processName : processName + "#" + index;
|
||||
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
|
||||
processIndexedName = index == 0 ? processName : processName + "#" + index;
|
||||
var processId = new PerformanceCounter("Process", "ID Process", processIndexedName);
|
||||
if ((int)processId.NextValue() == pid)
|
||||
{
|
||||
return processIndexdName;
|
||||
return processIndexedName;
|
||||
}
|
||||
}
|
||||
|
||||
return processIndexdName;
|
||||
return processIndexedName;
|
||||
}
|
||||
|
||||
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
|
||||
private static Process FindPidFromIndexedProcessName(string indexedProcessName, IEnumerable<Process> processes = null)
|
||||
{
|
||||
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
|
||||
return Process.GetProcessById((int)parentId.NextValue());
|
||||
var parentProcessId = (int)parentId.NextValue();
|
||||
|
||||
try
|
||||
{
|
||||
return processes == null ? Process.GetProcessById(parentProcessId) : processes.FirstOrDefault(p => p.Id == parentProcessId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Process Parent(this Process process)
|
||||
public static Process Parent(this Process process, IEnumerable<Process> processes = null)
|
||||
{
|
||||
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
|
||||
var indexedProcessName = FindIndexedProcessName(process.Id, process.ProcessName, processes);
|
||||
if (indexedProcessName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return FindPidFromIndexedProcessName(indexedProcessName, processes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,12 @@ namespace LgTv
|
||||
dolbyHdrCinema, dolbyHdrCinemaBright, dolbyHdrDarkAmazon, dolbyHdrGame, dolbyHdrStandard, dolbyHdrVivid, dolbyStandard
|
||||
}
|
||||
|
||||
public enum DynamicRange
|
||||
{
|
||||
sdr, hdr, technicolorHdr, dolbyHdr,
|
||||
sdrALLM, hdrALLM, technicolorHdrALLM, dolbyHdrALLM
|
||||
}
|
||||
|
||||
public enum OffToHigh
|
||||
{
|
||||
off,
|
||||
@@ -99,7 +105,9 @@ namespace LgTv
|
||||
|
||||
public enum FalseToTrue
|
||||
{
|
||||
[Description("Disabled")]
|
||||
false_,
|
||||
[Description("Enabled")]
|
||||
true_
|
||||
}
|
||||
|
||||
@@ -137,6 +145,62 @@ namespace LgTv
|
||||
mobile
|
||||
}
|
||||
|
||||
public enum SoundMode
|
||||
{
|
||||
[Description("AI Sound Pro")]
|
||||
aiSoundPlus,
|
||||
[Description("AI Sound")]
|
||||
aiSound,
|
||||
[Description("Standard")]
|
||||
standard,
|
||||
[Description("Clear Voice")]
|
||||
news,
|
||||
[Description("Music")]
|
||||
music,
|
||||
[Description("Cinema")]
|
||||
movie,
|
||||
[Description("Sports")]
|
||||
sports,
|
||||
[Description("Game Optimizer")]
|
||||
game,
|
||||
[Description("Pagode")]
|
||||
pagode,
|
||||
[Description("Serta Wego")]
|
||||
sertaWego,
|
||||
[Description("Brazilian Punk")]
|
||||
brazilianPunk,
|
||||
[Description("ASC")]
|
||||
asc,
|
||||
[Description("Bass Boost")]
|
||||
bass,
|
||||
}
|
||||
|
||||
public enum SoundOutput
|
||||
{
|
||||
[Description("TV Speaker")]
|
||||
tv_speaker,
|
||||
[Description("HDMI(ARC) Device")]
|
||||
external_arc,
|
||||
[Description("Optical Out Device")]
|
||||
external_optical,
|
||||
[Description("Bluetooth Device")]
|
||||
bt_soundbar,
|
||||
[Description("Mobile Device")]
|
||||
mobile_phone,
|
||||
[Description("Audio Out Device")]
|
||||
lineout,
|
||||
[Description("Wired Headphones")]
|
||||
headphone,
|
||||
[Description("Bluetooth Device + TV Speaker")]
|
||||
tv_speaker_bluetooth,
|
||||
[Description("Optical Out Device + TV Speaker")]
|
||||
tv_external_speaker,
|
||||
[Description("Wired Headphones + TV Speaker")]
|
||||
tv_speaker_headphone,
|
||||
[Description("WiSA Speakers")]
|
||||
wisa_speaker,
|
||||
}
|
||||
|
||||
public class LgTvApi:IDisposable
|
||||
{
|
||||
public bool ConnectionClosed { get => _connection?.ConnectionClosed ?? true; }
|
||||
@@ -600,6 +664,19 @@ namespace LgTv
|
||||
await ExecuteRequest(lunauri, @params);
|
||||
}
|
||||
|
||||
public async Task Reboot()
|
||||
{
|
||||
//await ExecuteRequest("luna://com.webos.service.oledepl/setGlobalStressReduction", new { enable = true });
|
||||
|
||||
//var requestMessage = new RequestMessage("ssap://com.webos.service.devicereset/requestReboot", new { reason = "broadcastSystemChanged" });
|
||||
//var response = await _connection.SendCommandAsync(requestMessage);
|
||||
|
||||
//await ExecuteRequest("luna://com.webos.settingsservice/setSystemSettings", new { category = "network", settings = new { wolwowlOnOff = "true" } });
|
||||
|
||||
//await ExecuteRequest("luna://com.webos.settingsservice/setSystemSettings", new { category = "general", settings = new { powerOffBySCA3SystemChanged = "true" } });
|
||||
//await ExecuteRequest("luna://com.webos.service.devicereset/requestReboot", new { reason = "broadcastSystemChanged" });
|
||||
}
|
||||
|
||||
private async Task ExecuteRequest(string lunauri, object @params)
|
||||
{
|
||||
var buttons = new[]
|
||||
@@ -661,6 +738,23 @@ namespace LgTv
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetSystemSettings2(string category)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
category = "dimensionInfo",
|
||||
//dimension = new { input = "default", _3dStatus = "2d", dynamicRange = "sdr" },
|
||||
keys = new[] { "input", "_3dStatus", "colorSystem", "dynamicRange", "gameInput" }
|
||||
};
|
||||
//await ExecuteRequest("luna://com.webos.settingsservice/getSystemSettings", payload);
|
||||
//return null;
|
||||
|
||||
var requestMessage = new RequestMessage("ssap://settings/getSystemSettings", payload);
|
||||
var command = _connection.SendCommandAsync(requestMessage);
|
||||
var res = await command;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_connection.Close();
|
||||
|
||||
Reference in new issue
Block a user