Skip to content

Commit 7a65441

Browse files
authored
Merge pull request #13 from evilC/AHK-v2
Ahk v2
2 parents a6d0676 + e77ac1d commit 7a65441

15 files changed

+565
-75
lines changed
File renamed without changes.

InterceptionTapHoldDemo.ahk AHK v1/Interception Subscription Mode Example.ahk

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
; Demonstrates Interception Subscription Mode with TapHoldManager
22
#SingleInstance force
33
; Use these includes if you placed the contents of the TapHoldManager and AutoHotInterception Lib folders in the AHK lib folder (My Documents\AutoHotkey\Lib)
4-
#include <AutoHotInterception>
5-
#include <InterceptionTapHold>
6-
#include <TapHoldManager>
4+
;#include <AutoHotInterception>
5+
;#include <InterceptionTapHold>
6+
;#include <TapHoldManager>
77

88
; Use these includes if you placed the contents of the TapHoldManager and AutoHotInterception Lib folders in a lib folder next to this script
99
; ie copy the AutoHotInterception Lib folder into the TapHoldManager Lib folder
10-
;#include Lib\AutoHotInterception.ahk
11-
;#include Lib\InterceptionTapHold.ahk
12-
;#include Lib\TapHoldManager.ahk
10+
#include Lib\AutoHotInterception.ahk
11+
#include Lib\InterceptionTapHold.ahk
12+
#include Lib\TapHoldManager.ahk
1313

1414
AHI := new AutoHotInterception()
15-
; keyboard1Id := AHI.GetDeviceIdFromHandle(false, "HID\VID_03EB&PID_FF02&REV_0008&MI_03")
16-
keyboard1Id := AHI.GetKeyboardId(0x03EB, 0xFF02)
15+
;keyboard1Id := AHI.GetKeyboardId(0x03EB, 0xFF02)
16+
;keyboard1Id := AHI.GetDeviceIdFromHandle(false, "HID\VID_03EB&PID_FF02&REV_0008&MI_03")
17+
keyboard1Id := 3
1718
ITH1 := new InterceptionTapHold(AHI, keyboard1Id)
1819

1920
ITH1.Add("1", Func("Func1"))

KeyboardToMouse.ahk AHK v1/KeyboardToMouse.ahk

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ HandleInput(axis, dir, isHold, taps, state := -1){
3232
; Tap
3333
x := 0
3434
y := 0
35-
%axis% := (dir * taps) * MoveMultiplier * MoveMultiplier
35+
%axis% := (dir * taps) * MoveMultiplier
3636
MouseMove, % x, % y, 0, R
3737
}
3838
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

AHK v2/Context Example.ahk

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#Requires AutoHotkey v2.0
2+
; Exmaple of Context Specific hotkeys (Only applies to a certain window)
3+
; If the library files are in a subfolder called Lib next to the script, use this
4+
#include Lib\TapHoldManager.ahk
5+
; If you placed all the library files in your My Documents\AutoHotkey\lib folder, use this
6+
;#include <TapHoldManager>
7+
8+
thm := TapHoldManager(,,,,"ahk_exe notepad.exe") ; with window parameter set here, default window criteria that will be set for all sub-created hotkeys under this manager object is notepad
9+
thm.Add("1", MyFunc1)
10+
thm.Add("2", MyFunc2)
11+
thm.Add("3", MyFunc3,,,,,"ahk_class WordPadClass") ; this hotkey's window criteria will be WordPad (instead of manager object's previously passed-in notepad default)
12+
13+
MyFunc1(isHold, taps, state){
14+
ToolTip("1`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state)
15+
SetTimer(RemoveTooltip, -1000)
16+
}
17+
18+
MyFunc2(isHold, taps, state){
19+
ToolTip("2`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state)
20+
SetTimer(RemoveTooltip, -1000)
21+
}
22+
23+
MyFunc3(isHold, taps, state){
24+
ToolTip("3`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state)
25+
SetTimer(RemoveTooltip, -1000)
26+
}
27+
28+
RemoveTooltip(){
29+
ToolTip
30+
}
31+
32+
^Esc::ExitApp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#Requires AutoHotkey v2.0
2+
#SingleInstance force
3+
4+
; Demonstrates Interception Subscription Mode with TapHoldManager
5+
6+
; Use these includes if you placed the contents of the TapHoldManager and AutoHotInterception Lib folders in the AHK lib folder (My Documents\AutoHotkey\Lib)
7+
; #include <AutoHotInterception>
8+
; #include <InterceptionTapHold>
9+
; #include <TapHoldManager>
10+
11+
; Use these includes if you placed the contents of the TapHoldManager and AutoHotInterception Lib folders in a lib folder next to this script
12+
; ie copy the AutoHotInterception Lib folder into the TapHoldManager Lib folder
13+
#include Lib\AutoHotInterception.ahk
14+
#include Lib\InterceptionTapHold.ahk
15+
#include Lib\TapHoldManager.ahk
16+
17+
AHI := AutoHotInterception()
18+
; keyboard1Id := AHI.GetDeviceIdFromHandle(false, "HID\VID_03EB&PID_FF02&REV_0008&MI_03")
19+
; keyboard1Id := AHI.GetKeyboardId(0x03EB, 0xFF02)
20+
keyboard1Id := 3
21+
ITH1 := InterceptionTapHold(AHI, keyboard1Id)
22+
23+
ITH1.Add("1", Func1)
24+
ITH1.Add("2", Func2)
25+
return
26+
27+
Func1(isHold, taps, state){
28+
ToolTip("KB 1 Key 1`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state)
29+
}
30+
31+
Func2(isHold, taps, state){
32+
ToolTip("KB 1 Key 2`n" (isHold ? "HOLD" : "TAP") "`nTaps: " taps "`nState: " state)
33+
}
34+
35+
^Esc::{
36+
ExitApp
37+
}

AHK v2/KeyboardToMouse.ahk

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#Requires AutoHotkey v2.0
2+
/*
3+
Remaps Keyboard Numpad to Mouse cursor movement
4+
Press and hold a key to move
5+
Double-tap and hold to move quicker
6+
Triple-tap and hold to move even quicker
7+
etc...
8+
*/
9+
#include Lib\TapHoldManager.ahk
10+
MoveMultiplier := 5
11+
InitMoveVectors()
12+
13+
thm := TapHoldManager()
14+
thm.Add("NumpadLeft", HandleInput.Bind("x", "-1"))
15+
thm.Add("NumpadRight", HandleInput.Bind("x", "1"))
16+
17+
thm.Add("NumpadUp", HandleInput.Bind("y", "-1"))
18+
thm.Add("NumpadDown", HandleInput.Bind("y", "1"))
19+
20+
HandleInput(axis, dir, isHold, taps, state := -1){
21+
global MoveVectors, MoveMultiplier
22+
ToolTip("isHold: " isHold ", Axis: " axis ", Dir: " dir ", Taps: " taps ", State: " state)
23+
if (state == 1){
24+
MoveVectors[axis] := (dir * taps) * MoveMultiplier
25+
SetTimer(DoMove, 10)
26+
} else if (state == 0){
27+
MoveVectors[axis] := 0
28+
if (MoveVectors["x"] == 0 && MoveVectors["y"] == 0){
29+
SetTimer(DoMove, 0)
30+
}
31+
} else {
32+
; Tap
33+
InitMoveVectors()
34+
MoveVectors[axis] := (dir * taps) * MoveMultiplier
35+
MouseMove(MoveVectors["x"], MoveVectors["y"], 0, "R")
36+
}
37+
}
38+
39+
InitMoveVectors(){
40+
global MoveVectors
41+
MoveVectors := Map("x", 0, "y", 0)
42+
}
43+
44+
DoMove(){
45+
global MoveVectors
46+
MouseMove(MoveVectors["x"], MoveVectors["y"] , 0, "R")
47+
}
48+
49+
^Esc::ExitApp

AHK v2/Lib/InterceptionTapHold.ahk

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#Requires AutoHotkey v2.0
2+
3+
; Patch for TapAndHoldManager to convert it to use Interception
4+
class InterceptionTapHold extends TapHoldManager {
5+
__New(ahi, id, tapTime?, holdTime?, maxTaps?, block := true){
6+
this.AHI := ahi
7+
this.isMouse := (id > 10)
8+
this.block := block
9+
this.id := id
10+
super.__New(tapTime?, holdTime?, maxTaps?)
11+
}
12+
13+
Add(keyName, callback, tapTime?, holdTime?, maxTaps?, block?){
14+
this.Bindings[keyName] := InterceptionKeyManager(this, keyName, callback, tapTime ?? this.tapTime, holdTime ?? this.holdTime, maxTaps ?? this.maxTaps, block ?? this.block)
15+
}
16+
17+
GetKeyboardList(){
18+
return this.AHI.GetDeviceList()
19+
}
20+
}
21+
22+
class InterceptionKeyManager extends TapHoldManager.KeyManager {
23+
mouseButtonIds := {LButton: 0, RButton: 1, MButton: 2, XButton1: 3, XButton2: 4}
24+
__New(manager, keyName, Callback, tapTime, holdTime, maxTaps, block){
25+
this.block := block ?? manager.block
26+
super.__New(manager, keyName, Callback, tapTime, holdTime, maxTaps)
27+
}
28+
29+
DeclareHotkeys(){
30+
this.SetState(1)
31+
}
32+
33+
SetState(state){
34+
if (this.manager.isMouse){
35+
if (!this.mouseButtonIds.HasKey(this.keyName)){
36+
MsgBox("Unknown Mouse Button name " this.keyName)
37+
ExitApp
38+
}
39+
keyName := this.mouseButtonIds[this.keyName]
40+
if (state)
41+
result := this.manager.AHI.SubscribeMouseButton(this.manager.id, keyName, this.block, this.KeyEvent.Bind(this))
42+
else
43+
result := this.manager.AHI.UnsubscribeMouseButton(this.manager.id, keyName)
44+
45+
} else {
46+
if (state)
47+
result := this.manager.AHI.SubscribeKey(this.manager.id, GetKeySC(this.keyName), this.block, this.KeyEvent.Bind(this))
48+
else
49+
result := this.manager.AHI.UnsubscribeKey(this.manager.id, GetKeySC(this.keyName))
50+
}
51+
}
52+
}

AHK v2/Lib/TapHoldManager.ahk

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#Requires AutoHotkey v2.0
2+
3+
class TapHoldManager {
4+
Bindings := Map()
5+
6+
__New(tapTime?, holdTime?, maxTaps := "", prefixes := "$", window := ""){
7+
this.tapTime := tapTime ?? 150
8+
this.holdTime := holdTime ?? this.tapTime
9+
this.maxTaps := maxTaps
10+
this.prefixes := prefixes
11+
this.window := window
12+
}
13+
14+
; Add a key
15+
Add(keyName, callback, tapTime?, holdTime?, maxTaps?, prefixes?, window?){
16+
if this.Bindings.Has(keyName)
17+
this.RemoveHotkey(keyName)
18+
this.Bindings[keyName] := TapHoldManager.KeyManager(this, keyName, callback, tapTime ?? this.tapTime, holdTime ?? this.holdTime, maxTaps?? this.maxTaps, prefixes?, window?)
19+
}
20+
21+
; Remove a key
22+
RemoveHotkey(keyName){
23+
this.Bindings.Delete(keyName).SetState(0)
24+
}
25+
26+
; Pause a key
27+
PauseHotkey(keyName){
28+
this.Bindings[keyName].SetState(0)
29+
}
30+
31+
; Unpause a key
32+
ResumeHotkey(keyName){
33+
this.Bindings[keyName].SetState(1)
34+
}
35+
36+
class KeyManager {
37+
; AutoHotInterception mod does not use prefixes or window, so these parameters must be optional
38+
__New(manager, keyName, callback, tapTime, holdTime, maxTaps, prefixes?, window?){
39+
this.state := 0 ; Current state of the key
40+
this.sequence := 0 ; Number of taps so far
41+
42+
this.holdWatcherState := 0 ; Are we potentially in a hold state?
43+
this.tapWatcherState := 0 ; Has a tap release occurred and another could possibly happen?
44+
45+
this.holdActive := 0 ; A hold was activated and we are waiting for the release
46+
47+
this.manager := manager
48+
this.keyName := keyName
49+
this.callback := callback
50+
this.tapTime := tapTime
51+
this.holdTime := holdTime
52+
this.maxTaps := maxTaps
53+
this.prefixes := prefixes ?? manager.prefixes
54+
this.window := window ?? manager.window
55+
56+
this.HoldWatcherFn := this.HoldWatcher.Bind(this)
57+
this.TapWatcherFn := this.TapWatcher.Bind(this)
58+
this.JoyReleaseFn := this.JoyButtonRelease.Bind(this)
59+
this.DeclareHotkeys()
60+
}
61+
62+
; Internal use only - declares hotkeys
63+
DeclareHotkeys(){
64+
if (this.window)
65+
HotIfWinactive this.window ; sets the hotkey window context if window option is passed-in
66+
67+
Hotkey this.prefixes this.keyName, this.KeyEvent.Bind(this, 1), "On" ; On option is important in case hotkey previously defined and turned off.
68+
if (this.keyName ~= "i)^\d*Joy"){
69+
Hotkey this.keyName " up", (*) => SetTimer(this.JoyReleaseFn, 10), "On"
70+
} else {
71+
Hotkey this.prefixes this.keyName " up", this.KeyEvent.Bind(this, 0), "On"
72+
}
73+
74+
if (this.window)
75+
HotIfWinactive ; restores hotkey window context to default
76+
}
77+
78+
; Turns On/Off hotkeys (should be previously declared) // state is either "1: On" or "0: Off"
79+
SetState(state){
80+
; "state" under this method context refers to whether the hotkey will be turned on or off, while in other methods context "state" refers to the current activity on the hotkey (whether it's pressed or released (after a tap or hold))
81+
if (this.window)
82+
HotIfWinactive this.window
83+
84+
state := (state ? "On" : "Off")
85+
Hotkey this.prefixes this.keyName, state
86+
if (this.keyName ~= "i)^\d*Joy"){
87+
Hotkey this.keyName " up", state
88+
} else {
89+
Hotkey this.prefixes this.keyName " up", state
90+
}
91+
92+
if (this.window)
93+
HotIfWinactive
94+
}
95+
96+
; For correcting a bug in AHK
97+
; A joystick button hotkey such as "1Joy1 up::" will fire on button down, and not on release up
98+
; So when the button is pressed, we start a timer which checks the actual state of the button using GetKeyState...
99+
; ... and when it is actually released, we fire the up event
100+
JoyButtonRelease(){
101+
if (GetKeyState(this.keyName))
102+
return
103+
SetTimer this.JoyReleaseFn, 0
104+
this.KeyEvent(0)
105+
}
106+
107+
; Called when key events (down / up) occur
108+
KeyEvent(state, *){
109+
if (state == this.state)
110+
return ; Suppress Repeats
111+
this.state := state
112+
if (state){
113+
; Key went down
114+
this.sequence++
115+
this.SetHoldWatcherState(1)
116+
} else {
117+
; Key went up
118+
this.SetHoldWatcherState(0)
119+
if (this.holdActive){
120+
this.holdActive := 0
121+
SetTimer this.FireCallback.Bind(this, this.sequence, 0), -1
122+
this.ResetSequence()
123+
return
124+
} else {
125+
if (this.maxTaps && this.Sequence == this.maxTaps){
126+
SetTimer this.FireCallback.Bind(this, this.sequence, -1), -1
127+
this.ResetSequence()
128+
} else {
129+
this.SetTapWatcherState(1)
130+
}
131+
}
132+
}
133+
}
134+
135+
; Resets everything once a sequence completes
136+
ResetSequence(){
137+
this.SetHoldWatcherState(0)
138+
this.SetTapWatcherState(0)
139+
this.sequence := 0
140+
this.holdActive := 0
141+
}
142+
143+
; When a key is pressed, if it is not released within tapTime, then it is considered a hold
144+
SetHoldWatcherState(state){
145+
this.holdWatcherState := state
146+
SetTimer this.HoldWatcherFn, (state ? "-" this.holdTime : 0)
147+
}
148+
149+
; When a key is released, if it is re-pressed within tapTime, the sequence increments
150+
SetTapWatcherState(state){
151+
this.tapWatcherState := state
152+
; SetTimer this.TapWatcherFn, (state ? "-" this.tapTime : 0)
153+
SetTimer this.TapWatcherFn, (state ? "-" this.tapTime : 0)
154+
}
155+
156+
; If this function fires, a key was held for longer than the tap timeout, so engage hold mode
157+
HoldWatcher(){
158+
if (this.sequence > 0 && this.state == 1){
159+
; Got to end of tapTime after first press, and still held.
160+
; HOLD PRESS
161+
SetTimer this.FireCallback.Bind(this, this.sequence, 1), -1
162+
this.holdActive := 1
163+
}
164+
}
165+
166+
; If this function fires, a key was released and we got to the end of the tap timeout, but no press was seen
167+
TapWatcher(){
168+
if (this.sequence > 0 && this.state == 0){
169+
; TAP
170+
SetTimer this.FireCallback.Bind(this, this.sequence), -1
171+
this.ResetSequence()
172+
}
173+
}
174+
175+
; Fires the user-defined callback
176+
FireCallback(seq, state := -1){
177+
this.Callback.Call(state != -1, seq, state)
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)