SubscribeKey POC

device-handlers
Clive Galway 2 years ago
parent 41d61fd3d7
commit 835fc71764

@ -46,9 +46,11 @@
<Compile Include="DeviceHandlers\MouseHandler.cs" />
<Compile Include="Helpers\HelperFunctions.cs" />
<Compile Include="Helpers\ManagedWrapper.cs" />
<Compile Include="MappingOptions.cs" />
<Compile Include="ScanCodeChecker.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WorkerThread.cs" />
</ItemGroup>
<ItemGroup>
<None Include="ToDo.md" />

@ -1,5 +1,6 @@
using AutoHotInterception.Helpers;
using System;
using System.Collections.Concurrent;
namespace AutoHotInterception.DeviceHandlers
{
@ -7,6 +8,9 @@ namespace AutoHotInterception.DeviceHandlers
{
protected IntPtr DeviceContext;
protected int _deviceId;
public bool IsFiltered { get; set; }
protected readonly ConcurrentDictionary<ushort, WorkerThread> WorkerThreads = new ConcurrentDictionary<ushort, WorkerThread>();
public DeviceHandler(IntPtr deviceContext, int deviceId)
{
@ -14,6 +18,7 @@ namespace AutoHotInterception.DeviceHandlers
_deviceId = deviceId;
}
public abstract void ProcessStroke(ManagedWrapper.Stroke stroke);
}
}

@ -1,18 +1,107 @@
using AutoHotInterception.Helpers;
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace AutoHotInterception.DeviceHandlers
{
class KeyboardHandler : DeviceHandler
{
private ConcurrentDictionary<ushort, MappingOptions> KeyboardKeyMappings = new ConcurrentDictionary<ushort, MappingOptions>();
public KeyboardHandler(IntPtr deviceContext, int deviceId) : base (deviceContext, deviceId)
{
IsFiltered = true;
}
public override void ProcessStroke(ManagedWrapper.Stroke stroke)
{
ManagedWrapper.Send(DeviceContext, _deviceId, ref stroke, 1);
//ManagedWrapper.Send(DeviceContext, _deviceId, ref stroke, 1);
var hasSubscription = false;
//var hasContext = ContextCallbacks.ContainsKey(i);
var hasContext = false;
// Process any waiting input for this keyboard
var block = false;
if (IsFiltered)
{
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 (KeyboardKeyMappings.ContainsKey(code))
{
isKeyMapping = true;
mapping = KeyboardKeyMappings[code];
}
if (mapping != null)
{
// Begin translation of incoming key code, state, extended flag etc...
var processMappings = true;
if (processedState.Ignore)
{
// Set flag to stop Context Mode from firing
hasSubscription = true;
// Set flag to indicate disable mapping processing
processMappings = false;
}
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[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 (block) return;
// If this key had no subscriptions, but Context Mode is set for this keyboard...
// ... then set the Context before sending the key
//if (!hasSubscription && hasContext) ContextCallbacks[i](1);
// Pass the key through to the OS.
ManagedWrapper.Send(DeviceContext, _deviceId, ref stroke, 1);
// If we are processing Context Mode, then Unset the context variable after sending the key
//if (!hasSubscription && hasContext) ContextCallbacks[i](0);
}
}
public void SubscribeKey(ushort code, MappingOptions mappingOptions)
{
KeyboardKeyMappings.TryAdd(code, mappingOptions);
if (!mappingOptions.Concurrent)
{
WorkerThreads.TryAdd(code, new WorkerThread());
WorkerThreads[code].Start();
}
}
}
}

@ -101,20 +101,23 @@ namespace AutoHotInterception
HelperFunctions.IsValidDeviceId(false, id);
SetFilterState(false);
if (!KeyboardKeyMappings.ContainsKey(id))
KeyboardKeyMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>());
var handler = (KeyboardHandler)DeviceHandlers[id];
handler.SubscribeKey(code, new MappingOptions { Block = block, Concurrent = concurrent, Callback = callback });
KeyboardKeyMappings[id].TryAdd(code,
new MappingOptions { Block = block, Concurrent = concurrent, Callback = callback });
//if (!KeyboardKeyMappings.ContainsKey(id))
// KeyboardKeyMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>());
if (!concurrent)
{
if (!WorkerThreads.ContainsKey(id))
WorkerThreads.TryAdd(id, new ConcurrentDictionary<ushort, WorkerThread>());
//KeyboardKeyMappings[id].TryAdd(code,
// new MappingOptions { Block = block, Concurrent = concurrent, Callback = callback });
WorkerThreads[id].TryAdd(code, new WorkerThread());
WorkerThreads[id][code].Start();
}
//if (!concurrent)
//{
// if (!WorkerThreads.ContainsKey(id))
// WorkerThreads.TryAdd(id, new ConcurrentDictionary<ushort, WorkerThread>());
// WorkerThreads[id].TryAdd(code, new WorkerThread());
// WorkerThreads[id][code].Start();
//}
SetDeviceFilterState(id, true);
SetFilterState(true);
@ -608,369 +611,324 @@ namespace AutoHotInterception
{
DeviceHandlers[i].ProcessStroke(stroke);
/*
if (i < 11)
{
// Keyboard
var isMonitoredKeyboard = IsMonitoredDevice(i) == 1;
var hasSubscription = false;
var hasContext = ContextCallbacks.ContainsKey(i);
// Process any waiting input for this keyboard
var block = false;
// 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)
if (false)
{
if (i < 11)
{
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))
// Keyboard
var isMonitoredKeyboard = IsMonitoredDevice(i) == 1;
var hasSubscription = false;
var hasContext = ContextCallbacks.ContainsKey(i);
// Process any waiting input for this keyboard
var block = false;
// 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)
{
mapping = KeyboardMappings[i];
}
else if (KeyboardKeyMappings.ContainsKey(i) && KeyboardKeyMappings[i].ContainsKey(code))
{
isKeyMapping = true;
mapping = KeyboardKeyMappings[i][code];
}
if (mapping != null)
{
// Process Subscription Mode
#region KeyCode, State, Extended Flag translation
// Begin translation of incoming key code, state, extended flag etc...
var processMappings = true;
#endregion
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 (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];
}
// Code and state now normalized, proceed with checking for subscriptions...
if (processMappings)
else if (KeyboardKeyMappings.ContainsKey(i) && KeyboardKeyMappings[i].ContainsKey(code))
{
hasSubscription = true;
isKeyMapping = true;
mapping = KeyboardKeyMappings[i][code];
}
if (mapping != null)
{
// Process Subscription Mode
if (mapping.Block) block = true;
if (mapping.Concurrent)
#region KeyCode, State, Extended Flag translation
// Begin translation of incoming key code, state, extended flag etc...
var processMappings = true;
#endregion
if (processedState.Ignore)
{
if (isKeyMapping)
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state));
}
else
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(code, state));
}
// Set flag to stop Context Mode from firing
hasSubscription = true;
// Set flag to indicate disable mapping processing
processMappings = false;
}
else
// Code and state now normalized, proceed with checking for subscriptions...
if (processMappings)
{
if (isKeyMapping)
hasSubscription = true;
if (mapping.Block) block = true;
if (mapping.Concurrent)
{
WorkerThreads[i][code]?.Actions.Add(() => mapping.Callback(state));
if (isKeyMapping)
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state));
}
else
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(code, state));
}
}
else
{
DeviceWorkerThreads[i]?.Actions.Add(() => mapping.Callback(code, state));
if (isKeyMapping)
{
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 (block) continue;
// If the key was blocked by Subscription Mode, then move on to next key...
if (block) continue;
// If this key had no subscriptions, but Context Mode is set for this keyboard...
// ... then set the Context before sending the key
if (!hasSubscription && hasContext) ContextCallbacks[i](1);
// If this key had no subscriptions, but Context Mode is set for this keyboard...
// ... then set the Context before sending the key
if (!hasSubscription && hasContext) ContextCallbacks[i](1);
// Pass the key through to the OS.
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
// Pass the key through to the OS.
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
// If we are processing Context Mode, then Unset the context variable after sending the key
if (!hasSubscription && hasContext) ContextCallbacks[i](0);
}
else
{
// Mice
var hasSubscription = false;
var hasContext = ContextCallbacks.ContainsKey(i);
var moveRemoved = false;
var hasMove = false;
var x = stroke.mouse.x;
var y = stroke.mouse.y;
//Debug.WriteLine($"AHK| Stroke Seen. State = {stroke.mouse.state}, Flags = {stroke.mouse.flags}, x={x}, y={y}");
// Process mouse movement
var isAbsolute = (stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute) ==
(ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute;
//Determine whether or not to report mouse movement.
// For Relative mode, this is fairly simple - if x and y are both 0, no movement was reported (Since a real mouse never reports x=0/y=0)
// For Absolute mode, x=0/y=0 is reported, but we should limit this to only reporting once...
// ... so when x=0/y=0 is seen in absolute mode, set the flag _absoluteMode00Reported to true and allow it to be reported...
// then on subsequent reports of x=0/y=0 for absolute mode, if _absoluteMode00Reported is already true, then do not report movement...
// ... In absolute mode, when x!=0/y!=0 is received, clear the _absoluteMode00Reported flag
if (isAbsolute)
// If we are processing Context Mode, then Unset the context variable after sending the key
if (!hasSubscription && hasContext) ContextCallbacks[i](0);
}
else
{
if (x == 0 && y == 0)
// Mice
var hasSubscription = false;
var hasContext = ContextCallbacks.ContainsKey(i);
var moveRemoved = false;
var hasMove = false;
var x = stroke.mouse.x;
var y = stroke.mouse.y;
//Debug.WriteLine($"AHK| Stroke Seen. State = {stroke.mouse.state}, Flags = {stroke.mouse.flags}, x={x}, y={y}");
// Process mouse movement
var isAbsolute = (stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute) ==
(ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute;
//Determine whether or not to report mouse movement.
// For Relative mode, this is fairly simple - if x and y are both 0, no movement was reported (Since a real mouse never reports x=0/y=0)
// For Absolute mode, x=0/y=0 is reported, but we should limit this to only reporting once...
// ... so when x=0/y=0 is seen in absolute mode, set the flag _absoluteMode00Reported to true and allow it to be reported...
// then on subsequent reports of x=0/y=0 for absolute mode, if _absoluteMode00Reported is already true, then do not report movement...
// ... In absolute mode, when x!=0/y!=0 is received, clear the _absoluteMode00Reported flag
if (isAbsolute)
{
if (!_absoluteMode00Reported)
if (x == 0 && y == 0)
{
hasMove = true;
_absoluteMode00Reported = true;
if (!_absoluteMode00Reported)
{
hasMove = true;
_absoluteMode00Reported = true;
}
else
{
hasMove = false;
}
}
else
{
hasMove = false;
hasMove = true;
_absoluteMode00Reported = false;
}
}
else
{
hasMove = true;
_absoluteMode00Reported = false;
hasMove = (x != 0 || y != 0);
}
}
else
{
hasMove = (x != 0 || y != 0);
}
if (hasMove)
{
// Process Absolute Mouse Move
if (isAbsolute)
if (hasMove)
{
if (MouseMoveAbsoluteMappings.ContainsKey(i))
// Process Absolute Mouse Move
if (isAbsolute)
{
var mapping = MouseMoveAbsoluteMappings[i];
hasSubscription = true;
//var debugStr = $"AHK| Mouse stroke has absolute move of {x}, {y}...";
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (WorkerThreads.ContainsKey(i) && WorkerThreads[i].ContainsKey(7))
WorkerThreads[i][7]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
{
moveRemoved = true;
stroke.mouse.x = 0;
stroke.mouse.y = 0;
//debugStr += "Blocking";
}
else
if (MouseMoveAbsoluteMappings.ContainsKey(i))
{
//debugStr += "Not Blocking";
}
var mapping = MouseMoveAbsoluteMappings[i];
hasSubscription = true;
//var debugStr = $"AHK| Mouse stroke has absolute move of {x}, {y}...";
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (WorkerThreads.ContainsKey(i) && WorkerThreads[i].ContainsKey(7))
WorkerThreads[i][7]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
{
moveRemoved = true;
stroke.mouse.x = 0;
stroke.mouse.y = 0;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
//Debug.WriteLine(debugStr);
//Debug.WriteLine(debugStr);
}
}
}
// Process Relative Mouse Move
//else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) / flag is 0, so always true!
else
{
if (MouseMoveRelativeMappings.ContainsKey(i))
// Process Relative Mouse Move
//else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) / flag is 0, so always true!
else
{
var mapping = MouseMoveRelativeMappings[i];
hasSubscription = true;
//var debugStr = $"AHK| Mouse stroke has relative move of {x}, {y}...";
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (WorkerThreads.ContainsKey(i) && WorkerThreads[i].ContainsKey(8))
WorkerThreads[i][8]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
if (MouseMoveRelativeMappings.ContainsKey(i))
{
moveRemoved = true;
stroke.mouse.x = 0;
stroke.mouse.y = 0;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
var mapping = MouseMoveRelativeMappings[i];
hasSubscription = true;
//var debugStr = $"AHK| Mouse stroke has relative move of {x}, {y}...";
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (WorkerThreads.ContainsKey(i) && WorkerThreads[i].ContainsKey(8))
WorkerThreads[i][8]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
{
moveRemoved = true;
stroke.mouse.x = 0;
stroke.mouse.y = 0;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
//Debug.WriteLine(debugStr);
//Debug.WriteLine(debugStr);
}
}
}
}
}
var isMouseButtonsMapping = MouseButtonsMappings.ContainsKey(i);
var isMouseButtonsMapping = MouseButtonsMappings.ContainsKey(i);
// Process Mouse Buttons - do this AFTER mouse movement, so that absolute mode has coordinates available at the point that the button callback is fired
if (stroke.mouse.state != 0 && MouseButtonMappings.ContainsKey(i) || isMouseButtonsMapping)
{
var btnStates = HelperFunctions.MouseStrokeToButtonStates(stroke);
foreach (var btnState in btnStates)
// Process Mouse Buttons - do this AFTER mouse movement, so that absolute mode has coordinates available at the point that the button callback is fired
if (stroke.mouse.state != 0 && MouseButtonMappings.ContainsKey(i) || isMouseButtonsMapping)
{
if (!isMouseButtonsMapping && !MouseButtonMappings[i].ContainsKey(btnState.Button))
continue;
hasSubscription = true;
MappingOptions mapping = null;
if (isMouseButtonsMapping)
var btnStates = HelperFunctions.MouseStrokeToButtonStates(stroke);
foreach (var btnState in btnStates)
{
mapping = MouseButtonsMappings[i];
}
else
{
mapping = MouseButtonMappings[i][btnState.Button];
}
var state = btnState;
if (!isMouseButtonsMapping && !MouseButtonMappings[i].ContainsKey(btnState.Button))
continue;
if (mapping.Concurrent)
{
hasSubscription = true;
MappingOptions mapping = null;
if (isMouseButtonsMapping)
{
ThreadPool.QueueUserWorkItem(threadProc =>
mapping.Callback(btnState.Button, state.State));
mapping = MouseButtonsMappings[i];
}
else
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
mapping = MouseButtonMappings[i][btnState.Button];
}
}
else
{
if (isMouseButtonsMapping)
var state = btnState;
if (mapping.Concurrent)
{
DeviceWorkerThreads[i]?.Actions
.Add(() => mapping.Callback(btnState.Button, state.State));
if (isMouseButtonsMapping)
{
ThreadPool.QueueUserWorkItem(threadProc =>
mapping.Callback(btnState.Button, state.State));
}
else
{
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
}
}
else
{
WorkerThreads[i][btnState.Button]?.Actions
.Add(() => mapping.Callback(state.State));
if (isMouseButtonsMapping)
{
DeviceWorkerThreads[i]?.Actions
.Add(() => mapping.Callback(btnState.Button, state.State));
}
else
{
WorkerThreads[i][btnState.Button]?.Actions
.Add(() => mapping.Callback(state.State));
}
}
}
if (mapping.Block)
{
// Remove the event for this button from the stroke, leaving other button events intact
stroke.mouse.state -= btnState.Flag;
// If we are removing a mouse wheel event, then set rolling to 0 if no mouse wheel event left
if (btnState.Flag == 0x400 || btnState.Flag == 0x800)
if (mapping.Block)
{
if ((stroke.mouse.state & 0x400) != 0x400 &&
(stroke.mouse.state & 0x800) != 0x800)
// Remove the event for this button from the stroke, leaving other button events intact
stroke.mouse.state -= btnState.Flag;
// If we are removing a mouse wheel event, then set rolling to 0 if no mouse wheel event left
if (btnState.Flag == 0x400 || btnState.Flag == 0x800)
{
//Debug.WriteLine("AHK| Removing rolling flag from stroke");
stroke.mouse.rolling = 0;
if ((stroke.mouse.state & 0x400) != 0x400 &&
(stroke.mouse.state & 0x800) != 0x800)
{
//Debug.WriteLine("AHK| Removing rolling flag from stroke");
stroke.mouse.rolling = 0;
}
}
//Debug.WriteLine($"AHK| Removing flag {btnState.Flag} from stoke, leaving state {stroke.mouse.state}");
}
else
{
//Debug.WriteLine($"AHK| Leaving flag {btnState.Flag} in stroke");
}
}
}
//Debug.WriteLine($"AHK| Removing flag {btnState.Flag} from stoke, leaving state {stroke.mouse.state}");
// Forward on the stroke if required
if (hasSubscription)
{
// Subscription mode
// If the stroke has a move that was not removed, OR it has remaining button events, then forward on the stroke
if ((hasMove && !moveRemoved) || stroke.mouse.state != 0)
{
//Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
}
else
{
//Debug.WriteLine($"AHK| Leaving flag {btnState.Flag} in stroke");
// Everything removed from stroke, do not forward
//Debug.WriteLine("AHK| Mouse stroke now empty, not forwarding");
}
}
}
// Forward on the stroke if required
if (hasSubscription)
{
// Subscription mode
// If the stroke has a move that was not removed, OR it has remaining button events, then forward on the stroke
if ((hasMove && !moveRemoved) || stroke.mouse.state != 0)
else if (hasContext)
{
//Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
// Context Mode - forward stroke with context wrapping
ContextCallbacks[i](1);
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
ContextCallbacks[i](0);
}
else
{
// Everything removed from stroke, do not forward
//Debug.WriteLine("AHK| Mouse stroke now empty, not forwarding");
// No subscription or context mode - forward on
//Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
}
//Debug.WriteLine($"AHK| ");
}
else if (hasContext)
{
// Context Mode - forward stroke with context wrapping
ContextCallbacks[i](1);
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
ContextCallbacks[i](0);
}
else
{
// No subscription or context mode - forward on
//Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
ManagedWrapper.Send(DeviceContext, i, ref stroke, 1);
}
//Debug.WriteLine($"AHK| ");
}
*/
}
}
_pollThreadRunning = false;
//Debug.WriteLine($"AHK| Poll Thread Ended");
}
internal class MappingOptions
{
public bool Block { get; set; }
public bool Concurrent { get; set; }
public dynamic Callback { get; set; }
}
internal class WorkerThread : IDisposable
{
private readonly Thread _worker;
private volatile bool _running;
public WorkerThread()
{
Actions = new BlockingCollection<Action>();
_worker = new Thread(Run);
_running = false;
}
public BlockingCollection<Action> Actions { get; }
public void Dispose()
{
if (!_running) return;
_running = false;
_worker.Join();
}
public void Start()
{
if (_running) return;
_running = true;
_worker.Start();
}
private void Run()
{
while (_running)
{
var action = Actions.Take();
action.Invoke();
}
}
}
#endregion
}
}

@ -0,0 +1,9 @@
namespace AutoHotInterception
{
class MappingOptions
{
public bool Block { get; set; }
public bool Concurrent { get; set; }
public dynamic Callback { get; set; }
}
}

@ -0,0 +1,48 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AutoHotInterception
{
class WorkerThread : IDisposable
{
private readonly Thread _worker;
private volatile bool _running;
public WorkerThread()
{
Actions = new BlockingCollection<Action>();
_worker = new Thread(Run);
_running = false;
}
public BlockingCollection<Action> Actions { get; }
public void Dispose()
{
if (!_running) return;
_running = false;
_worker.Join();
}
public void Start()
{
if (_running) return;
_running = true;
_worker.Start();
}
private void Run()
{
while (_running)
{
var action = Actions.Take();
action.Invoke();
}
}
}
}

@ -9,7 +9,7 @@ namespace TestApp
{
public class KeyboardKeyTester
{
public KeyboardKeyTester(TestDevice device, AhkKey key)
public KeyboardKeyTester(TestDevice device, AhkKey key, bool block = false)
{
Console.WriteLine($"Test key: {key.Name} - code {key.LogCode()}");
var im = new Manager();
@ -18,7 +18,7 @@ namespace TestApp
if (devId == 0) return;
im.SubscribeKey(devId, 0x2, false, new Action<int>(OnKeyEvent));
im.SubscribeKey(devId, 0x2, block, new Action<int>(OnKeyEvent));
}
public void OnKeyEvent(int value)

@ -10,7 +10,7 @@ namespace TestApp
//var mmt = new MouseMoveTester(TestDevices.LogitechWheelMouse);
//var mbt = new MouseButtonsTester(TestDevices.LogitechWheelMouse);
//var kt = new KeyboardTester(TestDevices.WyseKeyboard);
var kkt = new KeyboardKeyTester(TestDevices.WyseKeyboard, AhkKeys.Obj("1"));
var kkt = new KeyboardKeyTester(TestDevices.WyseKeyboard, AhkKeys.Obj("1"), true);
//var tt = new TabletTester(TestDevices.ParbloIslandA609);
//var sct = new ScanCodeTester(TestDevices.WyseKeyboard, true);
//var sst = new SetStateTester(TestDevices.WyseKeyboard, AhkKeys.Obj("1"));

Loading…
Cancel
Save