-
Notifications
You must be signed in to change notification settings - Fork 81
Expand file tree
/
Copy pathRollMouse.ahk
More file actions
189 lines (159 loc) · 5.26 KB
/
RollMouse.ahk
File metadata and controls
189 lines (159 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk
OutputDebug, DBGVIEWCLEAR
rm := new RollMouse(11)
return
class RollMouse {
; User configurable items
; The speed at which you must move the mouse to be able to trigger a roll
MoveThreshold := {x: 4, y: 4}
; Good value for my mouse with FPS games: 4
; Good value for my laptop trackpad: 3
; The speed at which to move the mouse, can be decimals (eg 0.5)
; X and Y do not need to be equal
; Good value for my mouse with FPS games: x:2, y: 1 (don't need vertical roll so much)
;~ MoveFactor := {x: 1, y: 1}
MoveFactor := {x: 0.5, y: 0.25}
; Good value for my laptop trackpad: 0.2
; How fast (in ms) to send moves when rolling.
; High values for this will cause rolls to appear jerky instead of smooth
; if you halved this, double MoveFactor to get the same move amount, but at a faster frequency.
RollFreq := 1
; How long to wait after each move to decide whether a roll has taken place.
TimeOutRate := 50
; The amount that we are currently rolling by
LastMove := {x: 0, y: 0}
; The number of previous moves stored - used to calculate vector of a roll
; Higher numbers = greater fidelity, but more CPU
MOVE_BUFFER_SIZE := 5
; Non user-configurable items
STATE_UNDER_THRESH := 1
STATE_OVER_THRESH := 2
STATE_ROLLING := 3
StateNames := ["UNDER THRESHOLD", "OVER THRESHOLD", "ROLLING"]
State := 1
TimeOutFunc := 0
History := {} ; Movement history. The most recent item is first (Index 1), and old (high index) items get pruned off the end
; Was an option in old RollMouse
Friction := 0
__New(mouseId){
this.TimeOutFunc := this.DoRoll.Bind(this)
this.AHI := new AutoHotInterception()
this.mouseId := mouseId
this.AHI.SubscribeMouseMove(this.mouseId, false, this.MouseMove.Bind(this))
}
MouseMove(x, y){
static axes := {x: 1, y: 2}
;~ ToolTip % x ", " y
moved := {x: 0, y: 0}
for axis, index in axes {
obj := {}
obj.delta_move := %axis%
obj.abs_delta_move := abs(obj.delta_move)
obj.sgn_move := (obj.abs_delta_move = obj.delta_move) ? 1 : -1
if (obj.abs_delta_move >= this.MoveThreshold[axis]){
moved[axis] := 1
}
this.UpdateHistory(axis, obj)
}
if (moved.x || moved.y){
; A move over the threshold was detected.
this.ChangeState(this.STATE_OVER_THRESH)
} else {
this.ChangeState(this.STATE_UNDER_THRESH)
}
}
UpdateHistory(axis, obj){
this.History[axis].InsertAt(1, obj)
; Enforce max number of entries
max := this.History[axis].Length()
if (max > (this.MOVE_BUFFER_SIZE - 1)){
this.History[axis].RemoveAt(max, max - this.MOVE_BUFFER_SIZE)
}
}
DoRoll(){
static axes := {x: 1, y: 2}
;s := ""
if (this.State != this.STATE_ROLLING){
; If roll has just started, calculate roll vector from movement history
this.LastMove := {x: 0, y: 0}
for axis in axes {
;s .= axis ": "
trend := 0
if (this.History[axis].Length() < this.MOVE_BUFFER_SIZE){
; ignore gestures that are too short
continue
}
Loop % this.History[axis].Length() {
if (A_Index != 1){
; Calculate the trend of the history.
trend += (this.History[axis][A_Index].delta_move - this.History[axis][A_Index-1].delta_move)
}
this.LastMove[axis] += this.History[axis][A_Index].delta_move
s .= this.History[axis][A_Index].delta_move ","
}
;s .= "(" trend ")`n"
/*
Disabled, as seems to break mouse trackpads.
Also seems to stop MoveFactor being applied to both axes?
if (sgn(trend) != sgn(this.History[axis][1].delta_move)){
; downward trend of move speed detected - this is probably a normal stop of the mouse, not a lift
continue
}
*/
this.LastMove[axis] := round(this.LastMove[axis] * this.MoveFactor[axis])
}
}
if (this.LastMove.x = 0 && this.LastMove.y = 0){
return
}
this.ChangeState(this.STATE_ROLLING)
this.Debug("ROLL DETECTED: " s "Rolling x: " this.LastMove.x ", y: " this.LastMove.y)
fn := this.MoveFunc
while (this.State == this.STATE_ROLLING){
; Send output
DllCall("user32.dll\mouse_event", "UInt", 0x0001, "UInt", this.LastMove.x, "UInt", this.LastMove.y, "UInt", 0, "UPtr", 0)
if (this.Friction){
this.LastMove.x := this.ApplyFriction(this.LastMove.x, this.Friction)
this.LastMove.y := this.ApplyFriction(this.LastMove.y, this.Friction)
if (this.LastMove.x == 0 && this.LastMove.y == 0){
this.State := this.STATE_UNDER_THRESH
break
}
}
; Wait for a bit (allow real mouse movement to be detected, which will turn off roll)
Sleep % this.RollFreq
}
}
ChangeState(newstate){
fn := this.TimeOutFunc
if (this.State != newstate){
this.Debug("Changing State to : " this.StateNames[newstate])
this.State := newstate
}
; DO NOT return if this.State == newstate!
; We need to reset the timer!
if (this.State = this.STATE_UNDER_THRESH){
; Kill the timer
SetTimer % fn, Off
; Clear the history
this.InitHistory()
} else if (this.State = this.STATE_OVER_THRESH){
; Mouse is moving fast - start timer to detect sudden stop in messages (mouse was lifted in motion)
SetTimer % fn, % this.TimeOutRate
}
/* else if (this.State = this.STATE_ROLLING){
;this.LastMove := {x: 0, y: 0}
}
*/
}
InitHistory(){
this.History := {x: [], y: []}
}
Debug(text){
OutputDebug % "AHK| " text
}
}
^Esc::
ExitApp