-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
mouse.lua
172 lines (150 loc) · 5.03 KB
/
mouse.lua
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
local mouse = {}
local knu = hs.loadSpoon("Knu")
local runtime = knu.runtime
-- Creates a hold-to-scroll button mode
--
--
-- holdToScrollButton([button[, scrollOffsetsFunction[, delay]]]) -> mode
--
-- Parameters:
--
-- - button - A button number. (0: left, 1: right, 2: middle, ...) Mouse movement while this button is held gets converted to scroll events. (default: 2)
-- - scrollOffsetsFunction - A function that takes three arguments (delta, origin, current) and returns { scrollX, scrollY }.
-- Parameters:
-- - delta - the change in the mouse position in pixels; a table containing x and y values
-- - origin - the mouse position where dragging started; a table containing x and y values
-- - current - the current mouse position; a table containing x and y values
-- - delay - Mouse dragging is ignored until this delay in milliseconds passes after the last mouse down. (default: 150)
--
-- Returns:
--
-- - A mode object that has two methods: enable() and disable().
mouse.holdToScrollButton = function (button, scrollOffsetsFunction, delay)
if button == nil then
button = 2
end
if scrollOffsetsFunction == nil then
scrollOffsetsFunction = function (delta, origin, current)
return { -delta.x, -delta.y }
end
end
if delay == nil then
delay = 150
end
local state = 0 -- 0: up, 1: down, 2: dragged
local origin
local downAt
local deltas = {}
local logger = hs.logger.new("knu.mouse")
local mouseDownEventType
local mouseUpEventType
local mouseDraggedEventType
if button == 0 then
-- Who would do this?
mouseDownEventType = hs.eventtap.event.types.leftMouseDown
mouseUpEventType = hs.eventtap.event.types.leftMouseUp
mouseDraggedEventType = hs.eventtap.event.types.leftMouseDragged
elseif button == 1 then
mouseDownEventType = hs.eventtap.event.types.rightMouseDown
mouseUpEventType = hs.eventtap.event.types.rightMouseUp
mouseDraggedEventType = hs.eventtap.event.types.rightMouseDragged
elseif button >= 2 then
mouseDownEventType = hs.eventtap.event.types.otherMouseDown
mouseUpEventType = hs.eventtap.event.types.otherMouseUp
mouseDraggedEventType = hs.eventtap.event.types.otherMouseDragged
else
error("button number must not be negative.")
end
local mouseDown = hs.eventtap.new({ mouseDownEventType }, function (e)
if e:getButtonState(button) then
if state == 0 then
downAt = hs.timer.absoluteTime()
origin = e:location()
deltas = {}
state = 1
return true
end
end
return false
end)
local mouseUp = hs.eventtap.new({ mouseUpEventType }, function (e)
if state ~= 0 and not e:getButtonState(button) then
if state == 1 then
local pos = e:location()
local dx = 0
local dy = 0
for _, delta in ipairs(deltas) do
dx = dx + delta.x
dy = dy + delta.y
end
state = 0
deltas = {}
hs.timer.doAfter(0, function ()
local pos = hs.mouse.absolutePosition()
pos.x = pos.x + dx
pos.y = pos.y + dy
hs.mouse.absolutePosition(pos)
local move = hs.eventtap.event.newEvent(hs.eventtap.event.types.mouseMoved, pos)
move:setProperty(hs.eventtap.event.properties.mouseEventDeltaX, dx)
move:setProperty(hs.eventtap.event.properties.mouseEventDeltaY, dy)
move:post()
end)
e:location(origin)
return true, {
e:copy():setType(mouseDownEventType),
e
}
elseif state == 2 then
state = 0
return true
end
end
return false
end)
local mouseDragged = hs.eventtap.new({ mouseDraggedEventType }, function (e)
if e:getButtonState(button) then
local pos = e:location()
hs.mouse.absolutePosition(pos)
deltas[#deltas + 1] = {
x = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaX),
y = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaY)
}
if state == 1 then
local millisecondsPassed = (hs.timer.absoluteTime() - downAt) / 1000000
if millisecondsPassed <= delay then
return true
end
end
local events = hs.fnutils.mapCat(
deltas,
function (delta)
local offsets = scrollOffsetsFunction(delta, origin, pos)
if offsets ~= nil then
return {
hs.eventtap.event.newScrollEvent(offsets, {}, "pixel")
}
else
return {}
end
end
)
state = 2
deltas = {}
return true, events
end
return false
end)
return runtime.guard({
enable = function (self)
mouseDown:start()
mouseUp:start()
mouseDragged:start()
end,
disable = function (self)
mouseDown:stop()
mouseUp:stop()
mouseDragged:stop()
end,
})
end
return mouse