Manager can subscribe to all keyboard keys

This commit is contained in:
Clive Galway 2019-11-14 18:53:00 +00:00
parent 9b558f98c2
commit 3577f5a920
5 changed files with 128 additions and 37 deletions

View File

@ -17,9 +17,12 @@ namespace AutoHotInterception
// Used by IsMonitoredDevice, which is handed to Interception as a "Predicate". // Used by IsMonitoredDevice, which is handed to Interception as a "Predicate".
private readonly ConcurrentDictionary<int, bool> _filteredDevices = new ConcurrentDictionary<int, bool>(); private readonly ConcurrentDictionary<int, bool> _filteredDevices = new ConcurrentDictionary<int, bool>();
private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>> _keyboardMappings = private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>> _keyboardKeyMappings =
new ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>>(); new ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>>();
private readonly ConcurrentDictionary<int, MappingOptions> _keyboardMappings =
new ConcurrentDictionary<int, MappingOptions>();
private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>> _mouseButtonMappings = private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>> _mouseButtonMappings =
new ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>>(); new ConcurrentDictionary<int, ConcurrentDictionary<ushort, MappingOptions>>();
@ -33,6 +36,8 @@ namespace AutoHotInterception
// Makes sure the events are handled synchronously and with a FIFO order. // Makes sure the events are handled synchronously and with a FIFO order.
private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, WorkerThread>> _workerThreads = private readonly ConcurrentDictionary<int, ConcurrentDictionary<ushort, WorkerThread>> _workerThreads =
new ConcurrentDictionary<int, ConcurrentDictionary<ushort, WorkerThread>>(); new ConcurrentDictionary<int, ConcurrentDictionary<ushort, WorkerThread>>();
private readonly ConcurrentDictionary<int, WorkerThread> _deviceWorkerThreads =
new ConcurrentDictionary<int, WorkerThread>();
private readonly MultimediaTimer _timer; private readonly MultimediaTimer _timer;
private readonly int _pollRate = 1; private readonly int _pollRate = 1;
@ -82,10 +87,10 @@ namespace AutoHotInterception
HelperFunctions.IsValidDeviceId(false, id); HelperFunctions.IsValidDeviceId(false, id);
SetFilterState(false); SetFilterState(false);
if (!_keyboardMappings.ContainsKey(id)) if (!_keyboardKeyMappings.ContainsKey(id))
_keyboardMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>()); _keyboardKeyMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>());
_keyboardMappings[id].TryAdd(code, _keyboardKeyMappings[id].TryAdd(code,
new MappingOptions {Block = block, Concurrent = concurrent, Callback = callback}); new MappingOptions {Block = block, Concurrent = concurrent, Callback = callback});
if (!concurrent) if (!concurrent)
@ -102,17 +107,33 @@ namespace AutoHotInterception
SetThreadState(true); SetThreadState(true);
} }
public void SubscribeKeyboard(int id, bool block, dynamic callback, bool concurrent = false)
{
HelperFunctions.IsValidDeviceId(false, id);
SetFilterState(false);
_keyboardMappings.TryAdd(id, new MappingOptions { Block = block, Concurrent = concurrent, Callback = callback });
if (!concurrent)
{
_deviceWorkerThreads.TryAdd(id, new WorkerThread());
_deviceWorkerThreads[id].Start();
}
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
public void UnsubscribeKey(int id, ushort code) public void UnsubscribeKey(int id, ushort code)
{ {
HelperFunctions.IsValidDeviceId(false, id); HelperFunctions.IsValidDeviceId(false, id);
SetFilterState(false); SetFilterState(false);
if (_keyboardMappings.TryGetValue(id, out var thisDevice)) if (_keyboardKeyMappings.TryGetValue(id, out var thisDevice))
{ {
thisDevice.TryRemove(code, out _); thisDevice.TryRemove(code, out _);
if (thisDevice.Count == 0) if (thisDevice.Count == 0)
{ {
_keyboardMappings.TryRemove(id, out _); _keyboardKeyMappings.TryRemove(id, out _);
SetDeviceFilterState(id, false); SetDeviceFilterState(id, false);
} }
} }
@ -482,7 +503,7 @@ namespace AutoHotInterception
private bool DeviceHasBindings(int id) private bool DeviceHasBindings(int id)
{ {
if (id < 11) if (id < 11)
return _keyboardMappings.ContainsKey(id); return _keyboardKeyMappings.ContainsKey(id);
return _mouseButtonMappings.ContainsKey(id) return _mouseButtonMappings.ContainsKey(id)
|| _mouseMoveRelativeMappings.ContainsKey(id) || _mouseMoveRelativeMappings.ContainsKey(id)
@ -509,7 +530,24 @@ namespace AutoHotInterception
// If this is not a monitored keyboard, skip. // If this is not a monitored keyboard, skip.
// This check should not really be needed as the IsMonitoredDevice() predicate should only match monitored keyboards... // This check should not really be needed as the IsMonitoredDevice() predicate should only match monitored keyboards...
// ... but in case it does, we want to ignore this bit and pass the input through // ... but in case it does, we want to ignore this bit and pass the input through
if (isMonitoredKeyboard && _keyboardMappings.ContainsKey(i)) if (isMonitoredKeyboard)
{
var isKeyMapping = false; // True if this is a mapping to a single key, else it would be a mapping to a whole device
var processedState = HelperFunctions.KeyboardStrokeToKeyboardState(stroke);
var code = processedState.Code;
var state = processedState.State;
MappingOptions mapping = null;
if (_keyboardMappings.ContainsKey(i))
{
mapping = _keyboardMappings[i];
}
else if (_keyboardKeyMappings.ContainsKey(i) && _keyboardKeyMappings[i].ContainsKey(code))
{
isKeyMapping = true;
mapping = _keyboardKeyMappings[i][code];
}
if (mapping != null)
{ {
// Process Subscription Mode // Process Subscription Mode
@ -517,7 +555,6 @@ namespace AutoHotInterception
// Begin translation of incoming key code, state, extended flag etc... // Begin translation of incoming key code, state, extended flag etc...
var processMappings = true; var processMappings = true;
var processedState = HelperFunctions.KeyboardStrokeToKeyboardState(stroke);
#endregion #endregion
@ -529,20 +566,36 @@ namespace AutoHotInterception
processMappings = false; processMappings = false;
} }
var code = processedState.Code;
var state = processedState.State;
// Code and state now normalized, proceed with checking for subscriptions... // Code and state now normalized, proceed with checking for subscriptions...
if (processMappings && _keyboardMappings[i].ContainsKey(code)) if (processMappings)
{ {
hasSubscription = true; hasSubscription = true;
var mapping = _keyboardMappings[i][code];
if (mapping.Block) block = true; if (mapping.Block) block = true;
if (mapping.Concurrent) if (mapping.Concurrent)
{
if (isKeyMapping)
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state)); ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state));
else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(code)) }
else
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(code, state));
}
}
else
{
if (isKeyMapping)
{
_workerThreads[i][code]?.Actions.Add(() => mapping.Callback(state)); _workerThreads[i][code]?.Actions.Add(() => mapping.Callback(state));
} }
else
{
_deviceWorkerThreads[i]?.Actions.Add(() => mapping.Callback(code, state));
}
}
}
}
} }
// If the key was blocked by Subscription Mode, then move on to next key... // If the key was blocked by Subscription Mode, then move on to next key...

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoHotInterception;
namespace TestApp
{
public class KeyboardKeyTester
{
public KeyboardKeyTester()
{
var im = new Manager();
var devId = im.GetKeyboardId(0x03EB, 0xFF02);
if (devId == 0) return;
im.SubscribeKey(devId, 0x1, false, new Action<int>(value =>
{
Console.WriteLine($"State: {value}");
}));
}
public void OnKeyEvent(int value)
{
Console.WriteLine($"State: {value}");
}
}
}

View File

@ -22,5 +22,10 @@ namespace TestApp
Console.WriteLine($"State: {value}"); Console.WriteLine($"State: {value}");
})); }));
} }
public void OnKeyEvent(int value)
{
Console.WriteLine($"State: {value}");
}
} }
} }

View File

@ -7,10 +7,10 @@ namespace TestApp
{ {
private static void Main() private static void Main()
{ {
var mt = new MouseTester(); //var mt = new MouseTester();
//var kt = new KeyboardTester(); var kt = new KeyboardKeyTester();
//var tt = new TabletTester(); //var tt = new TabletTester();
var mon = new MonitorTester(); //var mon = new MonitorTester();
Console.ReadLine(); Console.ReadLine();
} }
} }

View File

@ -43,6 +43,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="KeyboardTester.cs" /> <Compile Include="KeyboardTester.cs" />
<Compile Include="KeyboardKeyTester.cs" />
<Compile Include="MonitorTester.cs" /> <Compile Include="MonitorTester.cs" />
<Compile Include="MouseTester.cs" /> <Compile Include="MouseTester.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />