diff --git a/C#/AutoHotInterception/Manager.cs b/C#/AutoHotInterception/Manager.cs index 367cfa2..7deab12 100644 --- a/C#/AutoHotInterception/Manager.cs +++ b/C#/AutoHotInterception/Manager.cs @@ -17,9 +17,12 @@ namespace AutoHotInterception // Used by IsMonitoredDevice, which is handed to Interception as a "Predicate". private readonly ConcurrentDictionary _filteredDevices = new ConcurrentDictionary(); - private readonly ConcurrentDictionary> _keyboardMappings = + private readonly ConcurrentDictionary> _keyboardKeyMappings = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _keyboardMappings = + new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _mouseButtonMappings = new ConcurrentDictionary>(); @@ -33,6 +36,8 @@ namespace AutoHotInterception // Makes sure the events are handled synchronously and with a FIFO order. private readonly ConcurrentDictionary> _workerThreads = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _deviceWorkerThreads = + new ConcurrentDictionary(); private readonly MultimediaTimer _timer; private readonly int _pollRate = 1; @@ -82,10 +87,10 @@ namespace AutoHotInterception HelperFunctions.IsValidDeviceId(false, id); SetFilterState(false); - if (!_keyboardMappings.ContainsKey(id)) - _keyboardMappings.TryAdd(id, new ConcurrentDictionary()); + if (!_keyboardKeyMappings.ContainsKey(id)) + _keyboardKeyMappings.TryAdd(id, new ConcurrentDictionary()); - _keyboardMappings[id].TryAdd(code, + _keyboardKeyMappings[id].TryAdd(code, new MappingOptions {Block = block, Concurrent = concurrent, Callback = callback}); if (!concurrent) @@ -102,17 +107,33 @@ namespace AutoHotInterception 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) { HelperFunctions.IsValidDeviceId(false, id); SetFilterState(false); - if (_keyboardMappings.TryGetValue(id, out var thisDevice)) + if (_keyboardKeyMappings.TryGetValue(id, out var thisDevice)) { thisDevice.TryRemove(code, out _); if (thisDevice.Count == 0) { - _keyboardMappings.TryRemove(id, out _); + _keyboardKeyMappings.TryRemove(id, out _); SetDeviceFilterState(id, false); } } @@ -482,7 +503,7 @@ namespace AutoHotInterception private bool DeviceHasBindings(int id) { if (id < 11) - return _keyboardMappings.ContainsKey(id); + return _keyboardKeyMappings.ContainsKey(id); return _mouseButtonMappings.ContainsKey(id) || _mouseMoveRelativeMappings.ContainsKey(id) @@ -509,39 +530,71 @@ namespace AutoHotInterception // If this is not a monitored keyboard, skip. // 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 - if (isMonitoredKeyboard && _keyboardMappings.ContainsKey(i)) + if (isMonitoredKeyboard) { - // Process Subscription Mode - - #region KeyCode, State, Extended Flag translation - - // Begin translation of incoming key code, state, extended flag etc... - var processMappings = true; + 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; - #endregion - - if (processedState.Ignore) + if (_keyboardMappings.ContainsKey(i)) { - // Set flag to stop Context Mode from firing - hasSubscription = true; - // Set flag to indicate disable mapping processing - processMappings = false; + mapping = _keyboardMappings[i]; } + else if (_keyboardKeyMappings.ContainsKey(i) && _keyboardKeyMappings[i].ContainsKey(code)) + { + isKeyMapping = true; + mapping = _keyboardKeyMappings[i][code]; + } + if (mapping != null) + { + // Process Subscription Mode - var code = processedState.Code; - var state = processedState.State; + #region KeyCode, State, Extended Flag translation - // Code and state now normalized, proceed with checking for subscriptions... - if (processMappings && _keyboardMappings[i].ContainsKey(code)) - { - hasSubscription = true; - var mapping = _keyboardMappings[i][code]; - if (mapping.Block) block = true; - if (mapping.Concurrent) - ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state)); - else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(code)) - _workerThreads[i][code]?.Actions.Add(() => mapping.Callback(state)); + // Begin translation of incoming key code, state, extended flag etc... + var processMappings = true; + + #endregion + + if (processedState.Ignore) + { + // Set flag to stop Context Mode from firing + hasSubscription = true; + // Set flag to indicate disable mapping processing + processMappings = false; + } + + // Code and state now normalized, proceed with checking for subscriptions... + if (processMappings) + { + hasSubscription = true; + + if (mapping.Block) block = true; + if (mapping.Concurrent) + { + if (isKeyMapping) + { + ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state)); + } + else + { + ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(code, state)); + } + } + else + { + if (isKeyMapping) + { + _workerThreads[i][code]?.Actions.Add(() => mapping.Callback(state)); + } + else + { + _deviceWorkerThreads[i]?.Actions.Add(() => mapping.Callback(code, state)); + } + } + } } } diff --git a/C#/TestApp/KeyboardKeyTester.cs b/C#/TestApp/KeyboardKeyTester.cs new file mode 100644 index 0000000..4a6d231 --- /dev/null +++ b/C#/TestApp/KeyboardKeyTester.cs @@ -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(value => + { + Console.WriteLine($"State: {value}"); + })); + } + + public void OnKeyEvent(int value) + { + Console.WriteLine($"State: {value}"); + } + + } +} diff --git a/C#/TestApp/KeyboardTester.cs b/C#/TestApp/KeyboardTester.cs index 9050089..1648c11 100644 --- a/C#/TestApp/KeyboardTester.cs +++ b/C#/TestApp/KeyboardTester.cs @@ -22,5 +22,10 @@ namespace TestApp Console.WriteLine($"State: {value}"); })); } + + public void OnKeyEvent(int value) + { + Console.WriteLine($"State: {value}"); + } } } diff --git a/C#/TestApp/Program.cs b/C#/TestApp/Program.cs index 8651b6d..4e55ae7 100644 --- a/C#/TestApp/Program.cs +++ b/C#/TestApp/Program.cs @@ -7,10 +7,10 @@ namespace TestApp { private static void Main() { - var mt = new MouseTester(); - //var kt = new KeyboardTester(); + //var mt = new MouseTester(); + var kt = new KeyboardKeyTester(); //var tt = new TabletTester(); - var mon = new MonitorTester(); + //var mon = new MonitorTester(); Console.ReadLine(); } } diff --git a/C#/TestApp/TestApp.csproj b/C#/TestApp/TestApp.csproj index cd75c6a..cdb42d3 100644 --- a/C#/TestApp/TestApp.csproj +++ b/C#/TestApp/TestApp.csproj @@ -43,6 +43,7 @@ +