Skip to content

Fix: key command and key emulation #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
970 changes: 970 additions & 0 deletions src/HASS.Agent.Staging/HASS.Agent.Shared/Functions/Inputs.cs

Large diffs are not rendered by default.

32 changes: 18 additions & 14 deletions src/HASS.Agent.Staging/HASS.Agent.Shared/Functions/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
using System.Runtime.InteropServices;
using System.Text;
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.Functions
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal static class NativeMethods
public static class NativeMethods
{
internal static WINDOWPLACEMENT GetPlacement(IntPtr hwnd)
public static WINDOWPLACEMENT GetPlacement(IntPtr hwnd)
{
var placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
Expand All @@ -21,11 +22,11 @@ internal static WINDOWPLACEMENT GetPlacement(IntPtr hwnd)

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);

[Serializable]
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPLACEMENT
public struct WINDOWPLACEMENT
{
public int length;
public int flags;
Expand All @@ -40,33 +41,36 @@ internal struct WINDOWPLACEMENT
internal const uint SC_MONITORPOWER = 0xF170;

[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);
public static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hObject);
public static extern bool CloseHandle(IntPtr hObject);

internal delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);

[DllImport("USER32.DLL")]
internal static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

[DllImport("USER32.DLL")]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("USER32.DLL")]
internal static extern int GetWindowTextLength(IntPtr hWnd);
public static extern int GetWindowTextLength(IntPtr hWnd);

[DllImport("USER32.DLL")]
internal static extern bool IsWindowVisible(IntPtr hWnd);
public static extern bool IsWindowVisible(IntPtr hWnd);

[DllImport("USER32.DLL")]
internal static extern IntPtr GetShellWindow();
public static extern IntPtr GetShellWindow();

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, [Out] IntPtr lParam);
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, [Out] IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, [Out] IntPtr lParam);
public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, [Out] IntPtr lParam);

[DllImport("user32.dll")]
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using HASS.Agent.Shared.Enums;
using HASS.Agent.Shared.Models.HomeAssistant;
using Serilog;
using static HASS.Agent.Shared.Functions.Inputs;
using static HASS.Agent.Shared.Functions.NativeMethods;

namespace HASS.Agent.Shared.HomeAssistant.Commands
{
Expand All @@ -14,20 +18,11 @@ public class KeyCommand : AbstractCommand
{
private const string DefaultName = "key";

public const int KEYEVENTF_EXTENTEDKEY = 1;
public const int KEYEVENTF_KEYUP = 0;
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
public const int VK_VOLUME_MUTE = 0xAD;
public const int VK_VOLUME_UP = 0xAF;
public const int VK_VOLUME_DOWN = 0xAE;
public const int KEY_UP = 38;

public string State { get; protected set; }
public byte KeyCode { get; set; }

public VirtualKeyShort KeyCode { get; set; }

public KeyCommand(byte keyCode, string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(name ?? DefaultName, friendlyName ?? null, entityType, id)
public KeyCommand(VirtualKeyShort keyCode, string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(name ?? DefaultName, friendlyName ?? null, entityType, id)
{
KeyCode = keyCode;
State = "OFF";
Expand All @@ -52,9 +47,6 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Device = deviceConfig
};
}

[DllImport("user32.dll")]
public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);

public override string GetState() => State;

Expand All @@ -65,10 +57,23 @@ public override void TurnOff()

public override void TurnOn()
{
State = "OFF";
State = "ON";

var inputs = new INPUT[2];
inputs[0].type = InputType.INPUT_KEYBOARD;
inputs[0].U.ki.wVk = KeyCode;

inputs[1].type = InputType.INPUT_KEYBOARD;
inputs[1].U.ki.wVk = KeyCode;
inputs[1].U.ki.dwFlags = KEYEVENTF.KEYUP;

var ret = SendInput((uint)inputs.Length, inputs, INPUT.Size);
if (ret != inputs.Length)
{
var error = Marshal.GetLastWin32Error();
Log.Error($"[{DefaultName}] Error simulating key press for {KeyCode}: {error}");
}

keybd_event(KeyCode, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero);

State = "OFF";
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaMuteCommand : KeyCommand
{
private const string DefaultName = "mute";

public MediaMuteCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_VOLUME_MUTE, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaMuteCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.VOLUME_MUTE, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaNextCommand : KeyCommand
{
private const string DefaultName = "next";

public MediaNextCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_MEDIA_NEXT_TRACK, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaNextCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.MEDIA_NEXT_TRACK, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaPlayPauseCommand : KeyCommand
{
private const string DefaultName = "playpause";

public MediaPlayPauseCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_MEDIA_PLAY_PAUSE, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaPlayPauseCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.MEDIA_PLAY_PAUSE, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaPreviousCommand : KeyCommand
{
private const string DefaultName = "previous";

public MediaPreviousCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_MEDIA_PREV_TRACK, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaPreviousCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.MEDIA_PREV_TRACK, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaVolumeDownCommand : KeyCommand
{
private const string DefaultName = "volumedown";

public MediaVolumeDownCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_VOLUME_DOWN, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaVolumeDownCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.VOLUME_DOWN, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -9,6 +10,6 @@ public class MediaVolumeUpCommand : KeyCommand
{
private const string DefaultName = "volumeup";

public MediaVolumeUpCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VK_VOLUME_UP, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MediaVolumeUpCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(VirtualKeyShort.VOLUME_UP, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HASS.Agent.Shared.Enums;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.HomeAssistant.Commands.KeyCommands
{
Expand All @@ -10,6 +11,6 @@ public class MonitorWakeCommand : KeyCommand
{
private const string DefaultName = "monitorwake";

public MonitorWakeCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(KEY_UP, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
public MonitorWakeCommand(string name = DefaultName, string friendlyName = DefaultName, CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(VirtualKeyShort.UP, name ?? DefaultName, friendlyName ?? null, entityType, id) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using HASS.Agent.Shared.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using static HASS.Agent.Shared.Functions.Inputs;

namespace HASS.Agent.Shared.Models.Config
{
Expand All @@ -23,7 +24,7 @@ public class ConfiguredCommand

public string Command { get; set; } = string.Empty;

public byte KeyCode { get; set; }
public VirtualKeyShort KeyCode { get; set; }
public bool RunAsLowIntegrity { get; set; } = false;
public List<string> Keys { get; set; } = new List<string>();
}
Expand Down
17 changes: 10 additions & 7 deletions src/HASS.Agent.Staging/HASS.Agent/Forms/Commands/CommandsMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using HASS.Agent.Shared.Models.Config;
using HASS.Agent.Shared.Models.Internal;
using Newtonsoft.Json;
using static HASS.Agent.Shared.Functions.Inputs;
using Mono.Unix.Native;

namespace HASS.Agent.Forms.Commands
{
Expand Down Expand Up @@ -120,7 +122,7 @@ private void LoadCommand()
{
// load the card
var commandCard = CommandsManager.CommandInfoCards[Command.Type];

// select it as well
foreach (ListViewItem lvi in LvCommands.Items)
{
Expand Down Expand Up @@ -309,14 +311,15 @@ private void BtnStore_Click(object sender, EventArgs e)
ActiveControl = TbKeyCode;
return;
}
var parsed = int.TryParse(keycodeStr, out var keycode);

var parsed = Enum.TryParse(keycodeStr, out VirtualKeyShort enumKeycode);
if (!parsed)
{
MessageBoxAdv.Show(this, Languages.CommandsMod_BtnStore_MessageBox9, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
ActiveControl = TbKeyCode;
return;
}
Command.KeyCode = (byte)keycode;
Command.KeyCode = enumKeycode;
break;

case CommandType.MultipleKeysCommand:
Expand Down Expand Up @@ -436,7 +439,7 @@ private void BtnStore_Click(object sender, EventArgs e)
// done
DialogResult = DialogResult.OK;
}

private void LvCommands_SelectedIndexChanged(object sender, EventArgs e)
{
if (_loading) return;
Expand Down Expand Up @@ -630,7 +633,7 @@ private void SetUrlGui()

TbSetting.Text = string.Empty;
TbSetting.Visible = true;

CbCommandSpecific.CheckState = CheckState.Unchecked;
CbCommandSpecific.Text = Languages.CommandsMod_CbCommandSpecific_Incognito;

Expand Down Expand Up @@ -881,7 +884,7 @@ private void LblMqttTopic_Click(object sender, EventArgs e)

var item = (KeyValuePair<int, string>)CbEntityType.SelectedItem;
var entityType = (CommandEntityType)item.Key;

var deviceConfig = Variables.MqttManager?.GetDeviceConfigModel();
if (deviceConfig == null)
{
Expand Down Expand Up @@ -935,7 +938,7 @@ private void BtnConfigureCommand_Click(object sender, EventArgs e)

private void TbKeyCode_KeyDown(object sender, KeyEventArgs e)
{
TbKeyCode.Text = e.KeyValue.ToString();
TbKeyCode.Text = Enum.GetName(typeof(VirtualKeyShort), e.KeyValue);
}
}
}
1 change: 1 addition & 0 deletions src/HASS.Agent.Staging/HASS.Agent/Forms/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Serilog;
using Syncfusion.Windows.Forms;
using WK.Libraries.HotkeyListenerNS;
using NativeMethods = HASS.Agent.Functions.NativeMethods;
using QuickActionsConfig = HASS.Agent.Forms.QuickActions.QuickActionsConfig;
using Task = System.Threading.Tasks.Task;

Expand Down
Loading