@ -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
}
}