forked from abrasive/spacefn-evdev
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathspacefn.c
370 lines (323 loc) · 9.46 KB
/
spacefn.c
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
* spacefn-evdev.c
* James Laird-Wah (abrasive) 2018
* This code is in the public domain.
*/
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <libconfig.h>
// Debug flag
#define DEBUG 0
// Global device handles {{{1
struct libevdev *idev;
struct libevdev_uinput *odev;
int fd;
// Ordered unique key buffer {{{1
#define MAX_BUFFER 8
static unsigned int buffer[MAX_BUFFER];
static unsigned int n_buffer = 0;
enum key_io_functions {
V_RELEASE,
V_PRESS,
V_REPEAT
};
// State functions {{{1
enum {
IDLE,
DECIDE,
SHIFT,
} state = IDLE;
/* keys map */
#define MAX_KEYS_NUM 104
static int keys_map [MAX_KEYS_NUM][3];
/********** 按键 buffer 辅助操作函数 **********/
static int buffer_contains(unsigned int code) {
for (int i = 0; i < n_buffer; i++) {
if (buffer[i] == code) {
return 1;
}
}
return 0;
}
static int buffer_remove(unsigned int code) {
for (int i = 0; i < n_buffer; i++) {
if (buffer[i] == code) {
memcpy(&buffer[i], &buffer[i + 1], (n_buffer - i - 1) * sizeof(*buffer));
n_buffer--;
return 1;
}
}
return 0;
}
static int buffer_append(unsigned int code) {
if (n_buffer >= MAX_BUFFER) {
return 1;
}
buffer[n_buffer++] = code;
return 0;
}
// Key mapping {{{1
unsigned int key_map(unsigned int code, int* ext_code) {
int keys_map_len = sizeof(keys_map) / sizeof(keys_map[0]);
for (int i = 0; i < keys_map_len; i++) {
if (code != keys_map[i][0]) {
continue;
}
if (keys_map[i][2] != 0) {
*ext_code = keys_map[i][2];
}
return keys_map[i][1];
}
return 0;
}
// Blacklist keys for which I have a mapping, to try and train myself out of using them
int blacklist(unsigned int code) {
switch (code) {
case KEY_UP:
case KEY_DOWN:
case KEY_RIGHT:
case KEY_LEFT:
case KEY_HOME:
case KEY_END:
case KEY_PAGEUP:
case KEY_PAGEDOWN:
return 1;
}
return 0;
}
static void send_key(unsigned int code, int value) {
libevdev_uinput_write_event(odev, EV_KEY, code, value);
libevdev_uinput_write_event(odev, EV_SYN, SYN_REPORT, 0);
}
static int send_mapped_key(unsigned int code, int value) {
int ext_code = 0;
unsigned int mapped_code = key_map(code, &ext_code);
if (mapped_code) {
code = mapped_code;
}
// @todo 弹起时应该后发送
if (ext_code) {
send_key(ext_code, value);
ext_code = 0;
}
send_key(code, value);
return mapped_code;
}
static void send_press(unsigned int code) {
send_key(code, 1);
}
static void send_release(unsigned int code) {
send_key(code, 0);
}
static void send_repeat(unsigned int code) {
send_key(code, 2);
}
// input {{{2
static int read_one_key(struct input_event *ev) {
int err = libevdev_next_event(idev, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_BLOCKING, ev);
if (err) {
fprintf(stderr, "Failed: (%d) %s\n", -err, strerror(err));
exit(1);
}
if (ev->type != EV_KEY) {
libevdev_uinput_write_event(odev, ev->type, ev->code, ev->value);
return -1;
}
// 取消原作者的退出快捷键
//if (blacklist(ev->code)) {
// return -1;
//}
return 0;
}
static void state_idle(void) { // {{{2
if (DEBUG) {
printf("Enter IDLE\n");
}
struct input_event ev;
for (;;) {
while (read_one_key(&ev));
// 按下空格后进入 FN 决策状态
if (ev.code == KEY_SPACE && ev.value == V_PRESS) {
state = DECIDE;
return;
}
// 除按下空格键以外的都以正常键处理
send_key(ev.code, ev.value);
}
}
/* 空格键被按下,进入中间状态,需要判断是长按空格还是短按,长按才进入 FN 状态 */
static void state_decide(void) { // {{{2
if (DEBUG) {
printf("Enter DECIDE\n");
}
n_buffer = 0;
struct input_event ev;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 200000;
fd_set set;
FD_ZERO(&set);
while (timeout.tv_usec >= 0) {
FD_SET(fd, &set);
// 等待空格键之后的按键
int nfds = select(fd+1, &set, NULL, NULL, &timeout);
// 指定时间内为获得新的按键,进入超时处理逻辑
if (!nfds) {
break;
}
while (read_one_key(&ev));
// 记录空格状态下按下的所有键
if (ev.value == V_PRESS) {
buffer_append(ev.code);
continue;
}
// 如果空格在指定时间内弹起,则认定行为为单击空格,不做按键转换,直接退回 IDLE 状态
// 在该时间内按下的键都以正常按键处理
if (ev.code == KEY_SPACE && ev.value == V_RELEASE) {
send_key(KEY_SPACE, V_PRESS);
send_key(KEY_SPACE, V_RELEASE);
for (int i=0; i<n_buffer; i++)
send_key(buffer[i], V_PRESS);
state = IDLE;
return;
}
// 空格键按下前按下的键被释放,不做映射处理
if (ev.value == V_RELEASE && !buffer_contains(ev.code)) {
send_key(ev.code, ev.value);
continue;
}
// 空格键按下后按下的键,在超时前被松开
if (ev.value == V_RELEASE && buffer_remove(ev.code)) {
send_mapped_key(ev.code, V_PRESS);
send_mapped_key(ev.code, V_RELEASE);
state = SHIFT;
return;
}
}
// 超时处理逻辑,依次将 buffer 中的按键发出
for (int i=0; i<n_buffer; i++) {
// 查找按键映射,如果未映射,将原键发出
// 超时之前未弹起的按键都视为映射键
send_mapped_key(buffer[i], V_PRESS);
//send_mapped_key(buffer[i], V_RELEASE);
}
// 正式进入 FN 状态
state = SHIFT;
}
/* 正式进入 FN 状态 */
static void state_shift(void) {
if (DEBUG) {
printf("Enter SHIFT\n");
}
//n_buffer = 0;
struct input_event ev;
for (;;) {
while (read_one_key(&ev));
// 空格键被松开
if (ev.code == KEY_SPACE && ev.value == V_RELEASE) {
// 遍历 buffer 中的按键,依次发从弹起事件
for (int i=0; i<n_buffer; i++) {
send_mapped_key(buffer[i], V_RELEASE);
}
// 重新进入 IDLE 状态
state = IDLE;
return;
}
if (ev.code == KEY_SPACE)
continue;
int mapped_code = send_mapped_key(ev.code, ev.value);
if (mapped_code) {
// 由于增加了多键映射的缘故,这里必须保存原键才能保证信息不丢失
if (ev.value == V_PRESS) {
buffer_append(ev.code);
} else if (ev.value == V_RELEASE) {
buffer_remove(ev.code);
}
}
}
}
static void run_state_machine(void) {
for (;;) {
switch (state) {
case IDLE:
state_idle();
break;
case DECIDE:
state_decide();
break;
case SHIFT:
state_shift();
break;
}
}
}
int main(int argc, char **argv) { // {{{1
// 检查运行参数
if (argc < 2) {
printf("usage: %s config.cfg...", argv[0]);
return 1;
}
const config_setting_t *keys;
const config_setting_t *keyboard;
config_t cfg, *cf;
cf = &cfg;
config_init(cf);
if (!config_read_file(cf, argv[1])) {
fprintf(stderr, "read config file error %s:%d %s\n", config_error_file(cf), config_error_line(cf), config_error_text(cf));
config_destroy(cf);
return 1;
}
// 将配置文件中的参数存入 keys_map 数组
keys = config_lookup(cf, "keys_map");
int keys_count = config_setting_length(keys);
int max_num = keys_count > MAX_KEYS_NUM ? MAX_KEYS_NUM : keys_count;
for (int i = 0; i < max_num; i++) {
config_setting_t* key = config_setting_get_elem(keys, i);
if (config_setting_length(key) < 3) {
fprintf(stderr, "key map error for the %dth key, three code required\n", i + 1);
config_destroy(cf);
return 1;
}
for (int j = 0; j < 3; j++){
keys_map[i][j] = config_setting_get_int_elem(key, j);
}
}
// 键盘读取
keyboard = config_lookup(cf, "keyboard");
fd = open(config_setting_get_string(keyboard), O_RDONLY);
if (fd < 0) {
config_destroy(cf);
perror("open input");
return 1;
}
// 释放配置文件
config_destroy(cf);
int err = libevdev_new_from_fd(fd, &idev);
if (err) {
fprintf(stderr, "Failed: (%d) %s\n", -err, strerror(err));
return 1;
}
int uifd = open("/dev/uinput", O_RDWR);
if (uifd < 0) {
perror("open /dev/uinput");
return 1;
}
err = libevdev_uinput_create_from_device(idev, uifd, &odev);
if (err) {
fprintf(stderr, "Failed: (%d) %s\n", -err, strerror(err));
return 1;
}
// 修正无限回车的问题
usleep(200000);
err = libevdev_grab(idev, LIBEVDEV_GRAB);
if (err) {
fprintf(stderr, "Failed: (%d) %s\n", -err, strerror(err));
return 1;
}
run_state_machine();
}