Merge pull request #43 from evilC/hotfix/issue-39

Hotfix/issue 39
This commit is contained in:
Clive Galway 2019-07-09 14:37:42 +01:00 committed by GitHub
commit b86494316a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 156 additions and 77 deletions

View File

@ -55,18 +55,18 @@ namespace AutoHotInterception.Helpers
return stroke; return stroke;
} }
private static readonly Dictionary<int, ButtonState> ButtonStateLookupTable = new Dictionary<int, ButtonState>() private static readonly Dictionary<int, ButtonState> StrokeFlagToButtonState = new Dictionary<int, ButtonState>()
{ {
{ 1, new ButtonState{Button = 0, State = 1} }, { 1, new ButtonState{Button = 0, State = 1, Flag = 1} }, // LMB Press
{ 2, new ButtonState{Button = 0, State = 0} }, { 2, new ButtonState{Button = 0, State = 0, Flag = 2} }, // LMB Release
{ 4, new ButtonState{Button = 1, State = 1} }, { 4, new ButtonState{Button = 1, State = 1, Flag = 4} }, // RMB Press
{ 8, new ButtonState{Button = 1, State = 0} }, { 8, new ButtonState{Button = 1, State = 0, Flag = 8} }, // RMB Release
{ 16, new ButtonState{Button = 2, State = 1} }, { 16, new ButtonState{Button = 2, State = 1, Flag = 16} }, // MMB Press
{ 32, new ButtonState{Button = 2, State = 0} }, { 32, new ButtonState{Button = 2, State = 0, Flag = 32} }, // MMB Release
{ 64, new ButtonState{Button = 3, State = 1} }, { 64, new ButtonState{Button = 3, State = 1, Flag = 64} }, // XB1 Press
{ 128, new ButtonState{Button = 3, State = 0} }, { 128, new ButtonState{Button = 3, State = 0, Flag = 128} }, // XB1 Release
{ 256, new ButtonState{Button = 4, State = 1} }, { 256, new ButtonState{Button = 4, State = 1, Flag = 256} }, // XB2 Press
{ 512, new ButtonState{Button = 4, State = 0} }, { 512, new ButtonState{Button = 4, State = 0, Flag = 512} } // XB2 Release
}; };
public static ButtonState[] MouseStrokeToButtonStates(ManagedWrapper.Stroke stroke) public static ButtonState[] MouseStrokeToButtonStates(ManagedWrapper.Stroke stroke)
@ -75,7 +75,7 @@ namespace AutoHotInterception.Helpers
// Buttons // Buttons
var buttonStates = new List<ButtonState>(); var buttonStates = new List<ButtonState>();
foreach (var buttonState in ButtonStateLookupTable) foreach (var buttonState in StrokeFlagToButtonState)
{ {
if (state < buttonState.Key) break; if (state < buttonState.Key) break;
if ((state & buttonState.Key) != buttonState.Key) continue; if ((state & buttonState.Key) != buttonState.Key) continue;
@ -91,7 +91,8 @@ namespace AutoHotInterception.Helpers
new ButtonState new ButtonState
{ {
Button = 5, Button = 5,
State = (stroke.mouse.rolling < 0 ? -1 : 1) State = (stroke.mouse.rolling < 0 ? -1 : 1),
Flag = 0x400
} }
); );
} }
@ -101,7 +102,8 @@ namespace AutoHotInterception.Helpers
new ButtonState new ButtonState
{ {
Button = 6, Button = 6,
State = (stroke.mouse.rolling < 0 ? -1 : 1) State = (stroke.mouse.rolling < 0 ? -1 : 1),
Flag = 0x800
} }
); );
} }
@ -165,6 +167,7 @@ namespace AutoHotInterception.Helpers
{ {
public ushort Button { get; set; } public ushort Button { get; set; }
public int State { get; set; } public int State { get; set; }
public ushort Flag { get; set; } // Preserve original flag, so it can be removed from stroke
} }
public class KeyboardState public class KeyboardState

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading; using System.Threading;
using AutoHotInterception.Helpers; using AutoHotInterception.Helpers;
using static AutoHotInterception.Helpers.HelperFunctions; using static AutoHotInterception.Helpers.HelperFunctions;
@ -601,79 +602,147 @@ namespace AutoHotInterception
while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0) while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
{ {
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}"); if (!isMonitoredMouse) continue;
var block = false;
if (isMonitoredMouse) 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 Buttons
if (stroke.mouse.state != 0 && _mouseButtonMappings.ContainsKey(i))
{ {
if (stroke.mouse.state != 0 && _mouseButtonMappings.ContainsKey(i)) var btnStates = MouseStrokeToButtonStates(stroke);
foreach (var btnState in btnStates)
{ {
// Mouse Button if (!_mouseButtonMappings[i].ContainsKey(btnState.Button)) continue;
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var btnStates = MouseStrokeToButtonStates(stroke); hasSubscription = true;
foreach (var btnState in btnStates) var mapping = _mouseButtonMappings[i][btnState.Button];
var state = btnState;
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
else if (_workerThreads.ContainsKey(i) &&
_workerThreads[i].ContainsKey(btnState.Button))
_workerThreads[i][btnState.Button]?.Actions
.Add(() => mapping.Callback(state.State));
if (mapping.Block)
{ {
if (_mouseButtonMappings[i].ContainsKey(btnState.Button)) // 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)
{ {
hasSubscription = true; if ((stroke.mouse.state & 0x400) != 0x400 && (stroke.mouse.state & 0x800) != 0x800)
var mapping = _mouseButtonMappings[i][btnState.Button]; {
if (mapping.Block) block = true; //Debug.WriteLine("AHK| Removing rolling flag from stroke");
stroke.mouse.rolling = 0;
var state = btnState; }
if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
else if (_workerThreads.ContainsKey(i) &&
_workerThreads[i].ContainsKey(btnState.Button))
_workerThreads[i][btnState.Button]?.Actions
.Add(() => mapping.Callback(state.State));
} }
//Debug.WriteLine($"AHK| Removing flag {btnState.Flag} from stoke, leaving state {stroke.mouse.state}");
}
else
{
//Debug.WriteLine($"AHK| Leaving flag {btnState.Flag} in stroke");
} }
//Console.WriteLine($"AHK| Mouse {i} seen - button {btnState.Button}, state: {stroke.mouse.state}, rolling: {stroke.mouse.rolling}");
}
else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute) ==
(ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute
&& _mouseMoveAbsoluteMappings.ContainsKey(i))
{
// Absolute Mouse Move
hasSubscription = true;
var mapping = _mouseMoveAbsoluteMappings[i];
if (mapping.Block) block = true;
var x = stroke.mouse.x;
var y = stroke.mouse.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));
}
else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) ==
(ushort) ManagedWrapper.MouseFlag.MouseMoveRelative
&& _mouseMoveRelativeMappings.ContainsKey(i))
{
// Relative Mouse Move
hasSubscription = true;
var mapping = _mouseMoveRelativeMappings[i];
if (mapping.Block) block = true;
var x = stroke.mouse.x;
var y = stroke.mouse.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 this key had no subscriptions, but Context Mode is set for this mouse... // Process Relative Mouse Move
// ... then set the Context before sending the button if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative)
if (!hasSubscription && hasContext) _contextCallbacks[i](1); // Set Context {
if (!block) ManagedWrapper.Send(_deviceContext, i, ref stroke, 1); if (x != 0 || y != 0)
// If we are processing Context Mode, then Unset the context variable after sending the button {
if (!hasSubscription && hasContext) _contextCallbacks[i](0); hasMove = true;
if (_mouseMoveRelativeMappings.ContainsKey(i))
{
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;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
//Debug.WriteLine(debugStr);
}
}
}
// Process Absolute Mouse Move
else if ((stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute) == (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute)
{
if (x != 0 || y != 0)
{
hasMove = true;
if (_mouseMoveAbsoluteMappings.ContainsKey(i))
{
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;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
//Debug.WriteLine(debugStr);
}
}
}
// 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
{
// Everything removed from stroke, do not forward
//Debug.WriteLine("AHK| Mouse stroke now empty, not forwarding");
}
}
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| ");
} }
} }
// ToDo: Can this sleep be removed? Will removing it consume a lot of CPU? It will certainly make updates only happen once every ~10ms, which is too slow for mice polling @ 1khz
Thread.Sleep(10); Thread.Sleep(10);
} }
} }

View File

@ -5,12 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased] ## [Unreleased]
### Added ### Added
- Add option to filter key presses and only show key releases
### Changed ### Changed
### Deprecated ### Deprecated
### Removed ### Removed
### Fixed ### Fixed
- GUI layout made more robust
## [0.4.3] - 2019-06-10 **EXPERIMENTAL TEST RELEASE**
- Fixed issue #39
Almost complete rewrite of mouse polling code
Multiple event types (Movement, mouse button events) supported per update ("stroke") of the mouse
It is now possible to block a button or movement, but leave unblocked events unblocked
Previously, a stroke was either blocked or not - if any one part of the stroke was blocked, it was all blocked
- [Monitor script] GUI layout made more robust
- [Monitor script] Add option to filter key presses and only show key releases
## [0.4.2] - 2019-06-08 ## [0.4.2] - 2019-06-08
- Fixed issue #37 - Fixed issue #37