You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AutoHotInterception/README.md

468 lines
20 KiB
Markdown

6 years ago
![GitHub release](https://img.shields.io/github/release/evilc/autohotinterception.svg)
![Github All Releases](https://img.shields.io/github/downloads/evilc/autohotinterception/total.svg)
6 years ago
[![Discord](https://img.shields.io/discord/330423308103319562.svg)](https://discord.gg/9d3DNN4)
![Github commits (since latest release)](https://img.shields.io/github/commits-since/evilc/autohotinterception/latest.svg)
6 years ago
# AutoHotInterception
6 years ago
AutoHotInterception (AHI) allows you to execute AutoHotkey code in response to events from a *specific* keyboard or mouse, whilst (optionally) blocking the native functionality (i.e. stopping Windows from seeing that keyboard or mouse event).
In other words, you can use a key on a second (or third, or fourth...) keyboard to trigger AHK code, and that key will not be seen by applications. You can use the *same key* on multiple keyboards for individual actions.
For example, you could have 3 keyboards connected, and on the 1st (Main keyboard), no changes are applied, but on keyboard #2, when you press `F1`, it replaces it with `a`, and on keyboard #3, when you press `F1`, it replaces it with `b`.
Keyboard Keys, Mouse Buttons and Mouse movement (Both Relative and Absolute modes) are supported.
Both AHK v1 and AHK v2 are supported.
6 years ago
6 years ago
AHI uses the Interception driver by Francisco Lopez
# Getting Help
5 years ago
## [AHI Discussion Thread on the AHK forums](https://autohotkey.com/boards/viewtopic.php?f=6&t=45307)
## [Discord Channel](https://discord.gg/sFPMv86)
6 years ago
------
6 years ago
# WARNING
6 years ago
**TAKE CARE** when using this code. Because Interception is a driver, and sits below windows proper, blocking with Interception goes so deep that it can even block CTRL+ALT+DEL etc. As such, it is entirely possible to lock up all input, or at least make life a little difficult.
In general, worst-case scenario would require use of the reset button.
6 years ago
For example, using Subscription Mode with `block` enabled will **totally** block that key from working on that keyboard.
So if you block `Ctrl` on your only keyboard, you just blocked CTRL+ALT+DEL.
6 years ago
The best insurance policy is to have another keyboard or mouse handy, one that you don't block.
6 years ago
Be wary of making scripts using this code run on startup. Know how to enter "Safe Mode" in windows and disable startup of the scripts. Know mouse alternatives to emergency keyboard actions (Right click on clock for Task Manager!)
6 years ago
As they say - ***With great power comes great responsibility***.
6 years ago
If this all scares you and you don't really understand it, then TL/DR is you should probably stick to "Context Mode", it's safer.
6 years ago
------
# Device IDs / VIDs PIDs etc
6 years ago
Interception identifies unique devices by an ID. This is a number from 1..20.
Devices 1-10 are always keyboards
6 years ago
Devices 11-20 are always mice
This ID scheme is totally unique to Interception, and IDs may change as you plug / unplug devices etc.
On PC, devices are often identified by VendorID (VID) and ProductID (PID). These are identifiers baked into the hardware at time of manufacture, and are identical for all devices of the same make / model.
Most AHI functions (eg to Subscribe to a key etc) use an Interception ID, so some handy functions are provided to allow you to find the (current) Interception ID of your device, given a VID / PID.
If you are unsure of what the VID / PID of your device is (or even if Interception can see it), you can use the included Monitor script to find it.
5 years ago
You will need to know the VID / PID of at least one of your devices in order to do anything with AHI.
## Monitor App
This handy tool allows you to check if AHI is working, and also to find the VID/PID or DeviceHandle of your devices.
You can use the handy "Copy" buttons to copy the VID/PID or DeviceHandle of the device to the clipboard.
When using the monitor app, **DO NOT** tick all devices at once, as if it crashes, it will lock up all devices.
Instead, tick one at a time and see if it your device.
![](https://github.com/evilC/AutoHotInterception/blob/master/Monitor.png)
------
5 years ago
# Known Issues
If you unplug / replug a device, or go into hibernate and resume, the Interception ID of a device will increase by 1.
If the ID of a device goes above 10 (For keyboards) or 20 (For Mice), **The device will completely cease to function until the next reboot** - not only in AutoHotInterception, but in Windows also.
There is nothing I can do to fix this issue, it is a limitation of the Interception driver
------
6 years ago
# Setup
## Install the Intereception driver
Download and install the [Interception Driver](https://github.com/oblitum/Interception/releases)
5 years ago
Note that you **must** run `install-interception.exe` at an admin command prompt (**Not double-click it**) - once you do so, it will instruct you to execute `install-interception.exe /install` to actually perform the install.
Here is a GIF showing the process:
![](https://github.com/evilC/AutoHotInterception/blob/master/InterceptionInstall.gif)
## Build your AutoHotInterception folder
1. Download an AHI release from the [releases page](https://github.com/evilC/AutoHotInterception/releases) and extract it to a folder.
6 years ago
DO NOT use the "Clone or Download" link on the main page.
2. From the AHI release zip, extract **EITHER** the `AHK v1` folder **OR** the `AHK v2` folder to somewhere on your disk.
This is the "working folder" where (at least initially) you will be running scripts from.
It contains a number of sample `.ahk` scripts and a `lib` folder.
3. From the AHI release zip, extract `AutoHotInterception.dll` from the `Common\lib` folder and place it into `lib` in your "working folder"
4. In the Interception installer zip, there is a `library` folder containing `x86` and `x64` folders.
Copy both of these folders into the `lib` folder in your "working folder".
Example for AHK v1 - the "working folder" is on the left, top right is the AutoHotInterception zip, bottom right is the Interception zip.
![](https://github.com/evilC/AutoHotInterception/blob/master/FolderSetup.gif)
The folder structure should end up looking like:
5 years ago
```
AHI Root Folder
Monitor.ahk
etc...
Lib
AutoHotInterception.ahk
AutoHotInterception.dll
CLR.ahk
Unblocker.ps1
etc..
x86
interception.dll
x64
interception.dll
```
6 years ago
4. Right-click `Unblocker.ps1` in the lib folder and select `Run as Admin`.
This is because downloaded DLLs are often blocked and will not work.
Alternatively, this can be done manually in one of two ways:
1. By right clicking the DLLs, selecting Properties, and checking a "Block" box if it exists.
1. Before you open any zip (ie the AutoHotInterception zip or the Interception zip), right click it and select "Unblock".
5. Run `Monitor.ahk` from your working folder to check that everything works
5 years ago
6. (Optional) The contents of the `lib` folder can actually be placed in one of the AutoHotkey lib folders (eg `My Documents\AutoHotkey\lib` - make it if it does not exist), and the `#include` lines of the sample scripts changed to `#include <AutoHotInterception>`, to enable your AHI scripts to be in any folder, without each needing it's own copy of the library files.
6 years ago
------
6 years ago
6 years ago
# Usage
6 years ago
## Initializing the Library
### AHK v1
6 years ago
Include the library
```
#Persistent ; (Interception hotkeys do not stop AHK from exiting, so use this)
#include Lib\AutoHotInterception.ahk
```
Initialize the library
```
global AHI := new AutoHotInterception()
```
6 years ago
### AHK v2
Include the library
```
Persistent ; (Interception hotkeys do not stop AHK from exiting, so use this)
#include Lib\AutoHotInterception.ahk
```
Initialize the library
```
global AHI := AutoHotInterception()
```
6 years ago
*Note*
6 years ago
The `AHI` variable is an AHK class that makes it easy to interact with the AutoHotInterception DLL (Which itself then interacts with the Interception dll). For example, it wraps `GetDeviceList()` to make it return a normal AHK array. Most of the time you will not need it.
For advanced users, if you wish to directly communicate with the AHI DLL (eg for best possible performance), you can call `AHI.Instance` instead of `AHI` for most functions (eg when sending of synthesized input using `SendMouseMove`).
6 years ago
```
6 years ago
AHI := new AutoHotInterception()
AHI.Instance.SendMouseMove(...)
```
6 years ago
5 years ago
## Misc Commands
### SetState
`SetState(true|false)`
5 years ago
Turns on or off all subscriptions (Starts on)
Where `true` is on, `false` is off.
eg `AHI.SetState(false)`
6 years ago
## Finding Device IDs
### USB Devices
6 years ago
In most cases, you will want to hard-wire a script to a specific VID/PID - in this instance, use one of the following methods.
For all these methods, if you have multiple identical VID/PID devices, you can specify an `instance` (Starts from 1).
#### GetDeviceId
6 years ago
`AHI.GetDeviceId(<isMouse>, <VID>, <PID> [,<instance = 1>] )`
6 years ago
Where `isMouse` is `true` if you wish to find a mouse, or `false` if you wish to find a keyboard.
6 years ago
eg `AHI.GetDeviceId(false, 0x04F2, 0x0112)` to find a keyboard with VID 0x04F2 and PID 0x0112
6 years ago
#### GetKeyboardId
`AHI.GetKeyboardId(<VID>, <PID> [,<instance = 1>] )`
#### GetMouseId
`AHI.GetMouseId(<VID>, <PID> [,<instance = 1>] )`
6 years ago
### PS/2 and other Legacy devices (Can also apply to Laptops)
Some devices (eg older machines with PS/2 interfaces, or some laptops) may not use USB, so these will not have a VID and PID.
In this case, use the monitor app (Or `GetDeviceList()`) to findle out the "Handle" of your device, and get it's ID from that.
#### GetDeviceIdFromHandle
`AHI.GetDeviceIdFromHandle(<isMouse>, <handle> [,<instance = 1>] )`
6 years ago
This works in the same way as `GetDeviceId` above, except you pass a string containing the handle.
eg `AHI.GetDeviceIdFromHandle(false, "ACPI\PNP0303")` to find a keyboard with the handle `ACPI\PNP0303`
#### GetKeyboardIdFromHandle
`AHI.GetKeyboardIdFromHandle(<handle> [,<instance = 1>] )`
#### GetMouseIdFromHandle
`AHI.GetMouseIdFromHandle(<handle> [,<instance = 1>] )`
6 years ago
### Getting a list of devices
6 years ago
If you wish to get a list of all available devices, you can call `AHI.GetDeviceList()`, which will return an array of `DeviceInfo` objects, each of which has the following properties:
6 years ago
```
Id
isMouse
Vid
Pid
Handle
6 years ago
```
6 years ago
## Input Detection
AHI has two input detection modes - *Context Mode* and *Subscription Mode*, and both can be used simultaneously.
6 years ago
6 years ago
### Context mode
6 years ago
Context mode is so named as it takes advantage of AutoHotkey's [Context Sensitive Hotkeys](https://autohotkey.com/docs/Hotkeys.htm#Context).
6 years ago
As such, only Keyboard Keys and Mouse Buttons are supported in this mode. Mouse Movement is not supported.
6 years ago
In context mode, you create a *Context Manager* object which turns on/off a set of AHK hotkeys for you.
6 years ago
You wrap your hotkeys in an #if block which is controlled by the manager.
6 years ago
6 years ago
Create a Context Manager for the keyboard or mouse, pass it the Interception ID of the device.
6 years ago
Then Create your hotkeys, wrapped in an `#if` block that checks the `.IsActive` property of your Context Manager
(Complete, working script)
#### AHK v1
6 years ago
```
6 years ago
#include Lib\AutoHotInterception.ahk
AHI := new AutoHotInterception()
6 years ago
keyboard1Id := AHI.GetKeyboardId(0x04F2, 0x0112)
cm1 := AHI.CreateContextManager(keyboard1Id)
6 years ago
6 years ago
#if cm1.IsActive ; Start the #if block
6 years ago
::aaa::JACKPOT
1::
ToolTip % "KEY DOWN EVENT @ " A_TickCount
return
1 up::
ToolTip % "KEY UP EVENT @ " A_TickCount
return
6 years ago
#if ; Close the #if block
6 years ago
```
#### AHK v2
```
#include Lib\AutoHotInterception.ahk
AHI := AutoHotInterception()
keyboard1Id := AHI.GetKeyboardId(0x04F2, 0x0112)
cm1 := AHI.CreateContextManager(keyboard1Id)
#HotIf cm1.IsActive ; Start the #if block
::aaa::JACKPOT
1::
{
ToolTip("KEY DOWN EVENT @ " A_TickCount)
return
}
1 up::
{
ToolTip("KEY UP EVENT @ " A_TickCount)
return
}
#HotIf ; Close the #if block
```
You can remove a Context Manager using `AHI.RemoveContextManager(keyboard1Id)`
6 years ago
### Subscription mode
6 years ago
In Subscription mode, you bypass AHK's hotkey system completely, and Interception notifies you of key events via callbacks.
6 years ago
All forms of input are supported in Subscription Mode.
6 years ago
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.
SubscribeKey overrides SubscribeKeyboard - that is, if you have subscribed to all keys and a specific key on the same keyboard, then if you press the specific key, it's callback will fire and the callback for SubscribeKeyboard will not.
Each Subscribe endpoint also has a corresponding Unsubscribe endpoint, which removes the subscription and any block associated with it.
Both keyboard and mouse subscription functions have an optional `concurrent` parameter. This controls whether callbacks are fired sequentially or not.
False (Default) means that a new callback will not be fired until the last one has completed. This is especially useful for subscriptions involving mouse movement.
True means that a new thread will be used for each callback. If your callback has a long-running loop in it, this could mean that a callback could interrupt the previous callback, resulting in a steady buildup of callbacks (Read memory leak). Use at own risk!
6 years ago
6 years ago
#### Subscribing to Keyboard keys
4 years ago
##### Subscribe to a specific key on a specific keyboard
5 years ago
`SubscribeKey(<deviceId>, <scanCode>, <block>, <callback>, <concurrent>)`
`UnsubscribeKey(<deviceId>, <scanCode>)`
4 years ago
eg
###### AHK v1
4 years ago
`AHI.SubscribeKey(keyboardId, GetKeySC("1"), true, Func("KeyEvent"))`
6 years ago
6 years ago
Callback function is passed state `0` (released) or `1` (pressed)
6 years ago
```
KeyEvent(state){
ToolTip % "State: " state
}
```
4 years ago
###### AHK v2
`AHI.SubscribeKey(keyboardId, GetKeySC("1"), true, KeyEvent)`
Callback function is passed state `0` (released) or `1` (pressed)
```
KeyEvent(state){
ToolTip("State: " state)
}
```
4 years ago
##### Subscribe to all keys on a specific keyboard
`SubscribeKeyboard(<deviceId>, <block>, <callback>, <concurrent>)`
eg
###### AHK v1
4 years ago
`AHI.SubscribeKeyboard(keyboardId, true, Func("KeyEvent"))`
Callback function is passed scancode of pressed key and state
```
KeyEvent(code, state){
ToolTip % "Keyboard Key - Code: " code ", State: " state
}
```
###### AHK v2
`AHI.SubscribeKeyboard(keyboardId, true, KeyEvent)`
Callback function is passed scancode of pressed key and state
```
KeyEvent(code, state){
ToolTip("Keyboard Key - Code: " code ", State: " state)
}
```
6 years ago
#### Subscribing to Mouse Buttons
4 years ago
##### Subscribing to a specific button on a specific mouse
`SubscribeMouseButton(<deviceId>, <button>, <block>, <callback>, <concurrent>)`
5 years ago
`UnsubscribeMouseButton(<deviceId>, <button>)`
6 years ago
Where `button` is one of:
```
0: Left Mouse
1: Right Mouse
2: Middle Mouse
3: Side Button 1
4: Side Button 2
5: Mouse Wheel (Vertical)
6: Mouse Wheel (Horizontal)
```
For Mouse Wheel events, the `<state>` parameter will be `1` for Wheel Up / Right and `-1` for Wheel Down / Left
6 years ago
Otherwise, usage is identical to `SubscribeKey`
4 years ago
##### Subscribing to all buttons on a specific mouse
`SubscribeMouseButtons(<deviceId>, <block>, <callback>, <concurrent>)`
eg
###### AHK v1
4 years ago
`AHI.SubscribeMouseButtons(mouseId, true, Func("MouseButtonEvent"))`
Callback function is passed ID (See above) of pressed button and state
```
MouseButtonEvent(code, state){
ToolTip % "Mouse Button - Code: " code ", State: " state
}
```
###### AHK v2
`AHI.SubscribeMouseButtons(mouseId, true, MouseButtonEvent)`
Callback function is passed ID (See above) of pressed button and state
```
MouseButtonEvent(code, state){
ToolTip("Mouse Button - Code: " code ", State: " state)
}
```
6 years ago
#### Subscribing to Mouse Movement
**Warning!** When Subscribing to mouse movement, you will get **LOTS** of callbacks.
Note the CPU usage of the demo Monitor app.
AutoHotkey is *not good* for handling heavy processing in each callback (eg updating a GUI, like the monitor app does).
Keep your callbacks **short and efficient** in this mode if you wish to avoid high CPU usage.
##### Relative Mode
Relative mode is for normal mice and most trackpads.
5 years ago
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>)`
5 years ago
`UnsubscribeMouseMove(<deviceId>)`
`UnsubscribeMouseMoveRelative(<deviceId>)`
6 years ago
For Mouse Movement, the callback is passed two ints - x and y.
eg
###### AHK v1
6 years ago
```
AHI.SubscribeMouseMove(mouseId, false, Func("MouseEvent"))
6 years ago
MouseEvent(x, y){
[...]
}
```
6 years ago
###### AHK v2
```
AHI.SubscribeMouseMove(mouseId, false, MouseEvent)
MouseEvent(x, y){
[...]
}
```
6 years ago
##### Absolute Mode
Absolute mode is used for Graphics Tablets, Light Guns etc.
Coordinates will be in the range 0..65535
`SubscribeMouseMoveAbsolute(<deviceId>, <block>, <callback>, <concurrent>)`
5 years ago
`UnsubscribeMouseMoveAbsolute(<deviceId>)`
Again, the callback is passed two ints - x and y.
eg
###### AHK v1
6 years ago
```
AHI.SubscribeMouseMoveAbsolute(mouseId, false, Func("MouseEvent"))
6 years ago
MouseEvent(x, y){
[...]
}
```
###### AHK v2
```
AHI.SubscribeMouseMoveAbsolute(mouseId, false, MouseEvent)
MouseEvent(x, y){
[...]
}
```
6 years ago
## Synthesizing Output
Note that these commands will work in both Context and Subscription modes
Also note that you can send as any device, regardless of whether you have subscribed to it in some way or not.
### Sending Keyboard Keys
6 years ago
You can send keys as a specific keyboard using the `SendKeyEvent` method.
`AHI.SendKeyEvent(<keyboardId>, <scanCode>, <state>)`
6 years ago
scanCode = the Scan Code of the key
state = 1 for press, 0 for release
6 years ago
keyboardId = The Interception ID of the keyboard
6 years ago
```
AHI.SendKeyEvent(keyboardId, GetKeySC("a"), 1)
6 years ago
```
6 years ago
If you subscribe to a key using Subscription mode with the `block` parameter set to true, then send a different key using `SendKeyEvent`, you are transforming that key in a way which is totally invisible to windows (And all apps running on it), and it will respond as appropriate. For example, AHK `$` prefixed hotkeys **will not** be able to tell that this is synthetic input, and will respond to it.
6 years ago
### Sending Mouse Buttons
6 years ago
You can send clicks and other mouse button events with:
`AHI.SendMouseButtonEvent(<mouseId>, <button>, <state>)`
6 years ago
Where `button` is the button index, as used in `SubscribeMouseButton`
6 years ago
When Sending Mouse Wheel events, set `<state>` to `1` for Wheel Up / Right and `-1` for Wheel Down / Left.
6 years ago
If you are working in Absolute mode (eg with a graphics tablet or light guns), you can send mouse button events at specific coordinates using:
`AHI.SendMouseButtonEventAbsolute(<mouseId>, <button>, <state>, <x>, <y>)`
6 years ago
6 years ago
### Sending Mouse Movement
#### Relative
6 years ago
To send Relative (Normal) mouse movement, use:
`AHI.SendMouseMove(<mouseId>, <x>, <y>)`
5 years ago
X and Y are **not** setting the absolute cursor position, they are **altering current position**
Note that x and y are **not** in pixels, they are in "Mickeys"
6 years ago
#### Absolute
6 years ago
To sent Absolute mouse movement, use:
`AHI.SendMouseMoveAbsolute(<mouseId>, <x>, <y>)`
6 years ago
Note that Absolute mode will probably not work with FPS style mouse-aim games.
5 years ago
Note that Absolute mouse move uses coordinates in the range 0..65535 which are NOT screen coordinates. If, for example, you have one 1920x1080 monitor, then divide 65535 by 1920 to find the x position on your screen. This 65535 coordinate space maps to **all** your screens however, so if you have multiple monitors, further maths will be required.
6 years ago
5 years ago
#### Moving the Mouse Cursor
5 years ago
To move the mouse cursor to a specific screen or window coordinate, use:
5 years ago
`AHI.MoveCursor(<x>, <y> [, <coordMode>, <mouseId>])`
`coordMode` is optional and is the [CoordMode](https://www.autohotkey.com/docs/commands/CoordMode.htm) to use (Will switch back to current CoordMode after) - Defaults to "Screen".
5 years ago
`mouseId` is optional and the ID of the mouse to use (Defaults to ID 11 - the first mouse)
5 years ago
eg
`AHI.MoveCursor(100, 200)` - move to 100, 200 Screen position using mouse ID 11
`AHI.MoveCursor(100, 200, "Window")` - move to 100, 200 Window position using mouse ID 11
`AHI.MoveCursor(100, 200, , 12)` - move to 100, 200 Screen position using mouse ID 12
4 years ago
## Compiling scripts
AHI scripts can be compiled (Right click the script and select "Compile")
All required DLLs will be packed inside the EXE, so only the EXE needs to be distributed
When the EXE is run, the `Lib` folder will be created with the required DLLs