Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	C#/AutoHotInterception/Manager.cs
#	CHANGELOG.md
#	Lib/AutoHotInterception.ahk
#	README.md
pull/32/head
Jovan Nikolov 5 years ago
commit ffe11e1e6e

@ -71,15 +71,14 @@ namespace AutoHotInterception
/// <returns></returns>
public void SubscribeKey(int id, ushort code, bool block, dynamic callback, bool concurrent = false)
{
SetFilterState(false);
IsValidDeviceId(false, id);
SetFilterState(false);
if (!_keyboardMappings.ContainsKey(id))
_keyboardMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>());
_keyboardMappings[id].TryAdd(code,
new MappingOptions {Block = block, Concurrent = concurrent, Callback = callback});
_filteredDevices[id] = true;
if (!concurrent)
{
@ -90,6 +89,26 @@ namespace AutoHotInterception
_workerThreads[id][code].Start();
}
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
public void UnsubscribeKey(int id, ushort code)
{
IsValidDeviceId(false, id);
SetFilterState(false);
if (_keyboardMappings.TryGetValue(id, out var thisDevice))
{
thisDevice.TryRemove(code, out _);
if (thisDevice.Count == 0)
{
_keyboardMappings.TryRemove(id, out _);
SetDeviceFilterState(id, false);
}
}
SetFilterState(true);
SetThreadState(true);
}
@ -109,9 +128,9 @@ namespace AutoHotInterception
if (!_mouseButtonMappings.ContainsKey(id))
_mouseButtonMappings.TryAdd(id, new ConcurrentDictionary<ushort, MappingOptions>());
_mouseButtonMappings[id].TryAdd(btn,
new MappingOptions {Block = block, Concurrent = concurrent, Callback = callback});
_filteredDevices[id] = true;
if (!concurrent)
{
@ -122,6 +141,26 @@ namespace AutoHotInterception
_workerThreads[id][btn].Start();
}
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
public void UnsubscribeMouseButton(int id, ushort btn)
{
IsValidDeviceId(false, id);
SetFilterState(false);
if (_mouseButtonMappings.TryGetValue(id, out var thisDevice))
{
thisDevice.TryRemove(btn, out _);
if (thisDevice.Count == 0)
{
_mouseButtonMappings.TryRemove(id, out _);
SetDeviceFilterState(id, false);
}
}
SetFilterState(true);
SetThreadState(true);
}
@ -140,7 +179,6 @@ namespace AutoHotInterception
_mouseMoveAbsoluteMappings[id] = new MappingOptions
{Block = block, Concurrent = concurrent, Callback = callback};
_filteredDevices[id] = true;
if (!concurrent)
{
@ -151,16 +189,30 @@ namespace AutoHotInterception
_workerThreads[id][7].Start();
}
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
public void UnsubscribeMouseMoveAbsolute(int id)
{
IsValidDeviceId(true, id);
if (_mouseMoveAbsoluteMappings.TryRemove(id, out _))
if (!DeviceHasBindings(id))
SetDeviceFilterState(id, false);
}
//Shorthand for SubscribeMouseMoveRelative
public void SubscribeMouseMove(int id, bool block, dynamic callback, bool concurrent = false)
{
SubscribeMouseMoveRelative(id, block, callback, concurrent);
}
public void UnsubscribeMouseMove(int id)
{
UnsubscribeMouseMoveRelative(id);
}
/// <summary>
/// Subscribes to Relative mouse movement
/// </summary>
@ -175,7 +227,6 @@ namespace AutoHotInterception
_mouseMoveRelativeMappings[id] = new MappingOptions
{Block = block, Concurrent = concurrent, Callback = callback};
_filteredDevices[id] = true;
if (!concurrent)
{
@ -186,6 +237,19 @@ namespace AutoHotInterception
_workerThreads[id][8].Start();
}
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
public void UnsubscribeMouseMoveRelative(int id)
{
IsValidDeviceId(true, id);
if (_mouseMoveRelativeMappings.TryRemove(id, out _))
if (!DeviceHasBindings(id))
SetDeviceFilterState(id, false);
SetFilterState(true);
SetThreadState(true);
}
@ -207,8 +271,8 @@ namespace AutoHotInterception
throw new ArgumentOutOfRangeException(nameof(id), "DeviceIds must be between 1 and 20");
_contextCallbacks[id] = callback;
_filteredDevices[id] = true;
SetDeviceFilterState(id, true);
SetFilterState(true);
SetThreadState(true);
}
@ -438,6 +502,24 @@ namespace AutoHotInterception
state ? ManagedWrapper.Filter.All : ManagedWrapper.Filter.None);
}
private void SetDeviceFilterState(int device, bool state)
{
if (state && !_filteredDevices.ContainsKey(device))
_filteredDevices[device] = true;
else if (!state && _filteredDevices.ContainsKey(device))
_filteredDevices.TryRemove(device, out _);
}
private bool DeviceHasBindings(int id)
{
if (id < 11)
return _keyboardMappings.ContainsKey(id);
return _mouseButtonMappings.ContainsKey(id)
|| _mouseMoveRelativeMappings.ContainsKey(id)
|| _mouseMoveAbsoluteMappings.ContainsKey(id);
}
// ScanCode notes: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
private void PollThread()
{
@ -469,6 +551,10 @@ namespace AutoHotInterception
// Begin translation of incoming key code, state, extended flag etc...
var processMappings = true;
// Interception seems to report Right Shift as 54 / 0x36 with state 0/1...
// ... this code is normally unused (Alt-SysRq according to linked page) ...
// ... and AHK uses 54 + 256 = 310 (0x36 + 0x100 = 0x136)...
// ... so change the code, but leave the state as 0/1
if (code == 54) code = 310;
// If state is shifted up by 2 (1 or 2 instead of 0 or 1), then this is an "Extended" key code

@ -6,11 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
### Added
- Concurrency switch for executing subscription callback functions. Was implicitly executing on a new thread from the pool, now there is an option to execute each callback on a single thread (one worker per subscription).
- UnsubscribeKey, UnsubscribeMouseButton, UnsubscribeMouseMove, UnsubscribeMouseMoveRelative, UnsubscribeMouseMoveAbsolute methods added to Subscription Mode
- "Unsubscription Example.ahk" to demo Subscribe / Unsubscribe
### Changed
- By default the new concurrency switch will be set to false meaning that for every subscription there will be only a single worker thread and callbacks will be run sequentially.
### Deprecated
### Removed
### Fixed
- SubscribeMouseMove endpoint fixed to not return bool. (Fix "Can not implicitly convert type Void to object" error)
## [0.3.7] - 2018-12-11
- SubscribeMouseMove endpoint fixed to not return bool (Fix "Can not implicitly convert type Void to object" error)

@ -123,22 +123,42 @@ class AutoHotInterception {
this.Instance.SubscribeKey(id, code, block, callback, concurrent)
}
UnsubscribeKey(id, code){
this.Instance.UnsubscribeKey(id, code)
}
SubscribeMouseButton(id, btn, block, callback, concurrent := false) {
this.Instance.SubscribeMouseButton(id, btn, block, callback, concurrent)
}
UnsubscribeMouseButton(id, btn){
this.Instance.UnsubscribeMouseButton(id, btn)
}
SubscribeMouseMove(id, block, callback, concurrent := false) {
this.Instance.SubscribeMouseMove(id, block, callback, concurrent)
}
UnsubscribeMouseMove(id){
this.Instance.UnsubscribeMouseMove(id)
}
SubscribeMouseMoveRelative(id, block, callback, concurrent := false) {
this.Instance.SubscribeMouseMoveRelative(id, block, callback, concurrent)
}
UnsubscribeMouseMoveRelative(id){
this.Instance.UnsubscribeMouseMoveRelative(id)
}
SubscribeMouseMoveAbsolute(id, block, callback, concurrent := false) {
this.Instance.SubscribeMouseMoveAbsolute(id, block, callback, concurrent)
}
UnsubscribeMouseMoveAbsolute(id){
this.Instance.UnsubscribeMouseMoveAbsolute(id)
}
; ------------- Context Mode ----------------
; Creates a context class to make it easy to turn on/off the hotkeys
CreateContextManager(id) {

@ -161,10 +161,12 @@ cm1 := AHI.CreateContextManager(keyboard1Id)
In Subscription mode, you bypass AHK's hotkey system completely, and Interception notifies you of key events via callbacks.
All forms of input are supported in Subscription Mode.
Subscription Mode overrides Context Mode - that is, if a key on a keyboard has been subscribed to with Subscription Mode, then Context Mode will not fire for that key on that keyboard.
Each Subscribe endpont also has a corresponding Unsubscribe endpoint, which removes the subscription and any block associated with it.
#### Subscribing to Keyboard keys
Subscribe to a key on a specific keyboard
Subscribe to a key on a specific keyboard
`SubscribeKey(<deviceId>, <scanCode>, <block>, <callback>, <concurrent>)`
`UnsubscribeKey(<deviceId>, <scanCode>)`
```
Interception.SubscribeKey(keyboardId, GetKeySC("1"), true, Func("KeyEvent"))
return
@ -180,6 +182,7 @@ KeyEvent(state){
Parameter `<concurrent>` is optional and is <b>false</b> by default meaning that all the events raised for that key will be handled sequentially (i.e. callback function will be called on a single thread). If set to <b>true</b>, a new thread will be created for each event and the callback function will be called on it.
#### Subscribing to Mouse Buttons
`SubscribeMouseButton(<deviceId>, <button>, <block>, <callback>, <concurrent>)`
`UnsubscribeMouseButton(<deviceId>, <button>)`
Where `button` is one of:
```
0: Left Mouse
@ -202,8 +205,13 @@ Keep your callbacks **short and efficient** in this mode if you wish to avoid hi
##### Relative Mode
Relative mode is for normal mice and most trackpads.
Coordinates will be delta (change)
Coordinates will be delta (change)
Each endpoint has two naming variants for convenience, they both do the same.
`SubscribeMouseMove(<deviceId>, <block>, <callback>, <concurrent>)`
`SubscribeMouseMoveRelative(<deviceId>, <block>, <callback>, <concurrent>)`
`UnsubscribeMouseMove(<deviceId>)`
`UnsubscribeMouseMoveRelative(<deviceId>)`
For Mouse Movement, the callback is passed two ints - x and y.
```
Interception.SubscribeMouseMove(mouseId, false, Func("MouseEvent"))
@ -217,6 +225,7 @@ MouseEvent(x, y){
Absolute mode is used for Graphics Tablets, Light Guns etc.
Coordinates will be in the range 0..65535
`SubscribeMouseMoveAbsolute(<deviceId>, <block>, <callback>, <concurrent>)`
`UnsubscribeMouseMoveAbsolute(<deviceId>)`
Again, the callback is passed two ints - x and y.
```
Interception.SubscribeMouseMoveAbsolute(mouseId, false, Func("MouseEvent"))

@ -0,0 +1,38 @@
#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk
; Demonstrates Subscribe / Unsubscribe (Turn on/off block) dependent on active window
; Block is active in Notepad, inactive otherwise
AHI := new AutoHotInterception()
keyboardId := AHI.GetKeyboardId(0x04F2, 0x0112)
SetTimer, WatchWin, -0
return
KeyEvent(state){
ToolTip % "State: " state
}
DoSub(state){
global AHI, keyboardId
if (state){
AHI.SubscribeKey(keyboardId, GetKeySC("1"), true, Func("KeyEvent"))
} else {
AHI.UnsubscribeKey(keyboardId, GetKeySC("1"))
}
}
WatchWin:
Loop {
WinWaitActive, ahk_class Notepad
DoSub(true)
WinWaitNotActive, ahk_class Notepad
DoSub(false)
}
return
^Esc::
ExitApp
Loading…
Cancel
Save