-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhomerow.lua
More file actions
185 lines (158 loc) · 4.77 KB
/
homerow.lua
File metadata and controls
185 lines (158 loc) · 4.77 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
local eventtap = require "hs.eventtap"
local event = eventtap.event
local etype = event.types
local keycodes = require "hs.keycodes".map
local filter = require "hs.fnutils".filter
local concat = require "hs.fnutils".concat
local contains = require "hs.fnutils".contains
-- import this and call `init()` on it
local function diffTbl(t1, t2)
-- returns t1 without elements of t2
local function nc(e)
return not contains(t2, e)
end
return filter(t1, nc)
end
local function mergeTbl(t1, t2)
-- returns t1 with t2, no duplicates
return concat(t1, diffTbl(t2, t1))
end
hr = {}
-- Keycodes not present in `keycodes` table:
-- application key = 110
-- r_cmd = 54
-- l_cmd = 55
-- l_shift = 56
-- l_alt = 58
-- l_ctrl = 59
-- r_shift = 60
-- r_alt = 61
-- fn = 63
hr.config = {
activator = keycodes["space"],
-- held keybindings can not have a key
rebinds = {
h = {only={modifier={}, key="left"}},
j = {only={modifier={}, key="down"}},
k = {only={modifier={}, key="up"}},
l = {only={modifier={}, key="right"}},
i = {only={modifier={}, key="f"}},
a = {held={modifier={"shift"}},
only={modifier={"ctrl"}, key="s"}}
}
}
hr.state = {
other_key_pressed = false,
space_down = false,
modifiers = {},
down = {},
key_pressed_since = {}
}
-- a shorter keystroke action then the predefined one
local function shortKeyStroke(k)
-- do we need this?
local mods = eventtap.checkKeyboardModifiers()
hs.eventtap.event.newKeyEvent(mods, k, true):post()
hs.timer.usleep(50000)
hs.eventtap.event.newKeyEvent(mods, k, false):post()
end
local function handle_new_event(key, is_down_event)
local function set_mods(from)
if is_down_event then
hr.state.modifiers = mergeTbl(hr.state.modifiers, from)
else
hr.state.modifiers = diffTbl(hr.state.modifiers, from)
end
end
-- we already handled this one
if is_down_event and hr.state.down[key] then return end
local tbl = hr.config.rebinds[key]
-- handle bindings with a "hold down action"
if tbl.held then
if is_down_event then hr.state.down[key]=true end
set_mods(tbl.held.modifier)
-- if it is an up event
-- and no other key got pressed
-- activate `only` binding`
if (not is_down_event and
not hr.state.key_pressed_since[key] and
tbl.only) then
local mods = tbl.only.modifier
for _,k in ipairs(hr.state.modifiers) do
mods[k] = true
end
hs.eventtap.keyStroke(mods, tbl.only.key)
end
return
end
-- only activate `down event` if there is no `held` binding
if tbl.only then
set_mods(tbl.only.modifier)
return {hs.eventtap.event.newKeyEvent(hr.state.modifiers, tbl.only.key, is_down_event)}
end
end
-- this function takes an Event
-- and returns: <discard original event?>, <Events to post>
hr.process_key_event = function (evt)
local is_down_event = evt:getType() == etype.keyDown
local kc = evt:getKeyCode()
-- handle the modifier
if kc == hr.config.activator then
-- down event
if is_down_event then
hr.state.other_key_pressed = false
hr.state.space_down = true
return true,{}
-- up event
else
hr.state.space_down=false
hr.state.modifiers={}
hr.state.down={}
-- we did not press any other key, so send `space`
if not hr.state.other_key_pressed then
hr.eventtap:stop() -- else we start a loop
shortKeyStroke("space")
hr.eventtap:start()
end
return true,{}
end -- is_down_event
-- handle other keys
else
-- if we are not in `home-row-mode` keep the events
if not hr.state.space_down then
return false, evt
end
-- set key pressed since down-events for every handled key
if is_down_event then
-- only set other_key_pressed if it is a down event
-- (to prevent <keydown> <space down> <keyup> <space up> to result in errors)
hr.state.other_key_pressed = true
for tk, tv in pairs(hr.state.down) do
if tk ~= keycodes[kc] and tv then
hr.state.key_pressed_since[tk] = true
end
end
end
-- now handle home row rebinds
if hr.config.rebinds[keycodes[kc]] then
local new_evt = handle_new_event(keycodes[kc], is_down_event)
if not is_down_event then
hr.state.down[keycodes[kc]] = false
hr.state.key_pressed_since[keycodes[kc]] = false
end
if new_evt then
return true, new_evt
end
return true, {}
end
-- should the default event have homerow modifiers?
-- evt:setFlags(...)
return false, evt
end
end
function init_module()
hr.eventtap = eventtap.new({etype.keyUp, etype.keyDown}, hr.process_key_event)
-- hr.eventtap:start()
end
hr.init = init_module()
return home_row