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;
}
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} },
{ 2, new ButtonState{Button = 0, State = 0} },
{ 4, new ButtonState{Button = 1, State = 1} },
{ 8, new ButtonState{Button = 1, State = 0} },
{ 16, new ButtonState{Button = 2, State = 1} },
{ 32, new ButtonState{Button = 2, State = 0} },
{ 64, new ButtonState{Button = 3, State = 1} },
{ 128, new ButtonState{Button = 3, State = 0} },
{ 256, new ButtonState{Button = 4, State = 1} },
{ 512, new ButtonState{Button = 4, State = 0} },
{ 1, new ButtonState{Button = 0, State = 1, Flag = 1} }, // LMB Press
{ 2, new ButtonState{Button = 0, State = 0, Flag = 2} }, // LMB Release
{ 4, new ButtonState{Button = 1, State = 1, Flag = 4} }, // RMB Press
{ 8, new ButtonState{Button = 1, State = 0, Flag = 8} }, // RMB Release
{ 16, new ButtonState{Button = 2, State = 1, Flag = 16} }, // MMB Press
{ 32, new ButtonState{Button = 2, State = 0, Flag = 32} }, // MMB Release
{ 64, new ButtonState{Button = 3, State = 1, Flag = 64} }, // XB1 Press
{ 128, new ButtonState{Button = 3, State = 0, Flag = 128} }, // XB1 Release
{ 256, new ButtonState{Button = 4, State = 1, Flag = 256} }, // XB2 Press
{ 512, new ButtonState{Button = 4, State = 0, Flag = 512} } // XB2 Release
};
public static ButtonState[] MouseStrokeToButtonStates(ManagedWrapper.Stroke stroke)
@ -75,7 +75,7 @@ namespace AutoHotInterception.Helpers
// Buttons
var buttonStates = new List<ButtonState>();
foreach (var buttonState in ButtonStateLookupTable)
foreach (var buttonState in StrokeFlagToButtonState)
{
if (state < buttonState.Key) break;
if ((state & buttonState.Key) != buttonState.Key) continue;
@ -91,7 +91,8 @@ namespace AutoHotInterception.Helpers
new ButtonState
{
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
{
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 int State { get; set; }
public ushort Flag { get; set; } // Preserve original flag, so it can be removed from stroke
}
public class KeyboardState

@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using AutoHotInterception.Helpers;
using static AutoHotInterception.Helpers.HelperFunctions;
@ -601,79 +602,147 @@ namespace AutoHotInterception
while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
{
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var block = false;
if (isMonitoredMouse)
if (!isMonitoredMouse) continue;
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
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var btnStates = MouseStrokeToButtonStates(stroke);
foreach (var btnState in btnStates)
if (!_mouseButtonMappings[i].ContainsKey(btnState.Button)) continue;
hasSubscription = true;
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;
var mapping = _mouseButtonMappings[i][btnState.Button];
if (mapping.Block) block = true;
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 ((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");
}
}
}
//Console.WriteLine($"AHK| Mouse {i} seen - button {btnState.Button}, state: {stroke.mouse.state}, rolling: {stroke.mouse.rolling}");
// Process Relative Mouse Move
if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative)
{
if (x != 0 || y != 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);
}
}
else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute) ==
(ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute
&& _mouseMoveAbsoluteMappings.ContainsKey(i))
}
// Process Absolute Mouse Move
else if ((stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute) == (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute)
{
if (x != 0 || y != 0)
{
// Absolute Mouse Move
hasSubscription = true;
var mapping = _mouseMoveAbsoluteMappings[i];
if (mapping.Block) block = true;
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);
}
}
}
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));
// 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 if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) ==
(ushort) ManagedWrapper.MouseFlag.MouseMoveRelative
&& _mouseMoveRelativeMappings.ContainsKey(i))
else
{
// 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));
// Everything removed from stroke, do not forward
//Debug.WriteLine("AHK| Mouse stroke now empty, not forwarding");
}
}
// If this key had no subscriptions, but Context Mode is set for this mouse...
// ... then set the Context before sending the button
if (!hasSubscription && hasContext) _contextCallbacks[i](1); // Set Context
if (!block) ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
// If we are processing Context Mode, then Unset the context variable after sending the button
if (!hasSubscription && hasContext) _contextCallbacks[i](0);
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);
}
}

@ -5,12 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
### Added
- Add option to filter key presses and only show key releases
### Changed
### Deprecated
### Removed
### 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
- Fixed issue #37

Loading…
Cancel
Save