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

Hotfix/issue 39
hotfix/absolute-mouse
Clive Galway 5 years ago committed by GitHub
commit b86494316a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

@ -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,22 +602,23 @@ 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))
{ {
// Mouse Button
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var btnStates = MouseStrokeToButtonStates(stroke); var btnStates = MouseStrokeToButtonStates(stroke);
foreach (var btnState in btnStates) foreach (var btnState in btnStates)
{ {
if (_mouseButtonMappings[i].ContainsKey(btnState.Button)) if (!_mouseButtonMappings[i].ContainsKey(btnState.Button)) continue;
{
hasSubscription = true; hasSubscription = true;
var mapping = _mouseButtonMappings[i][btnState.Button]; var mapping = _mouseButtonMappings[i][btnState.Button];
if (mapping.Block) block = true;
var state = btnState; var state = btnState;
@ -626,54 +628,121 @@ namespace AutoHotInterception
_workerThreads[i].ContainsKey(btnState.Button)) _workerThreads[i].ContainsKey(btnState.Button))
_workerThreads[i][btnState.Button]?.Actions _workerThreads[i][btnState.Button]?.Actions
.Add(() => mapping.Callback(state.State)); .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 ((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}");
//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) == else
(ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute {
&& _mouseMoveAbsoluteMappings.ContainsKey(i)) //Debug.WriteLine($"AHK| Leaving flag {btnState.Flag} in stroke");
}
}
}
// Process Relative Mouse Move
if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative)
{ {
// Absolute Mouse Move if (x != 0 || y != 0)
{
hasMove = true;
if (_mouseMoveRelativeMappings.ContainsKey(i))
{
var mapping = _mouseMoveRelativeMappings[i];
hasSubscription = true; hasSubscription = true;
var mapping = _mouseMoveAbsoluteMappings[i]; //var debugStr = $"AHK| Mouse stroke has relative move of {x}, {y}...";
if (mapping.Block) block = true;
var x = stroke.mouse.x;
var y = stroke.mouse.y;
if (mapping.Concurrent) if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y)); ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(7)) else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(8))
_workerThreads[i][7]?.Actions.Add(() => mapping.Callback(x, y)); _workerThreads[i][8]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
{
moveRemoved = true;
//debugStr += "Blocking";
} }
else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == else
(ushort) ManagedWrapper.MouseFlag.MouseMoveRelative
&& _mouseMoveRelativeMappings.ContainsKey(i))
{ {
// Relative Mouse Move //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; hasSubscription = true;
var mapping = _mouseMoveRelativeMappings[i]; //var debugStr = $"AHK| Mouse stroke has absolute move of {x}, {y}...";
if (mapping.Block) block = true;
var x = stroke.mouse.x;
var y = stroke.mouse.y;
if (mapping.Concurrent) if (mapping.Concurrent)
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y)); ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(8)) else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(7))
_workerThreads[i][8]?.Actions.Add(() => mapping.Callback(x, y)); _workerThreads[i][7]?.Actions.Add(() => mapping.Callback(x, y));
if (mapping.Block)
{
moveRemoved = true;
//debugStr += "Blocking";
}
else
{
//debugStr += "Not Blocking";
}
//Debug.WriteLine(debugStr);
}
} }
} }
// If this key had no subscriptions, but Context Mode is set for this mouse... // Forward on the stroke if required
// ... then set the Context before sending the button if (hasSubscription)
if (!hasSubscription && hasContext) _contextCallbacks[i](1); // Set Context {
if (!block) ManagedWrapper.Send(_deviceContext, i, ref stroke, 1); // Subscription mode
// If we are processing Context Mode, then Unset the context variable after sending the button // If the stroke has a move that was not removed, OR it has remaining button events, then forward on the stroke
if (!hasSubscription && hasContext) _contextCallbacks[i](0); 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);
} }
} }

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

Loading…
Cancel
Save