mirror of
https://gitlab.com/interception/linux/plugins/caps2esc.git
synced 2025-04-04 22:09:24 +00:00
141 lines
4.7 KiB
C
141 lines
4.7 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
#include <linux/input.h>
|
|
|
|
// clang-format off
|
|
const struct input_event
|
|
syn = {.type = EV_SYN , .code = SYN_REPORT , .value = 0},
|
|
esc_up = {.type = EV_KEY , .code = KEY_ESC , .value = 0},
|
|
ctrl_up = {.type = EV_KEY , .code = KEY_LEFTCTRL , .value = 0},
|
|
esc_down = {.type = EV_KEY , .code = KEY_ESC , .value = 1},
|
|
ctrl_down = {.type = EV_KEY , .code = KEY_LEFTCTRL , .value = 1};
|
|
// clang-format on
|
|
|
|
void print_usage(FILE *stream, const char *program) {
|
|
// clang-format off
|
|
fprintf(stream,
|
|
"caps2esc - transforming the most useless key ever in the most useful one\n"
|
|
"\n"
|
|
"usage: %s [-h | [-m mode] [-t delay]]\n"
|
|
"\n"
|
|
"options:\n"
|
|
" -h show this message and exit\n"
|
|
" -t delay used for key sequences (default: 20000 microseconds)\n"
|
|
" -m mode 0: default\n"
|
|
" - caps as esc/ctrl\n"
|
|
" - esc as caps\n"
|
|
" 1: minimal\n"
|
|
" - caps as esc/ctrl\n"
|
|
" 2: useful on 60%% layouts\n"
|
|
" - caps as esc/ctrl\n"
|
|
" - esc as grave accent\n"
|
|
" - grave accent as caps\n",
|
|
program);
|
|
// clang-format on
|
|
}
|
|
|
|
int read_event(struct input_event *event) {
|
|
return fread(event, sizeof(struct input_event), 1, stdin) == 1;
|
|
}
|
|
|
|
void write_event(const struct input_event *event) {
|
|
if (fwrite(event, sizeof(struct input_event), 1, stdout) != 1)
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void write_event_with_mode(struct input_event *event, int mode) {
|
|
if (event->type == EV_KEY)
|
|
switch (mode) {
|
|
case 0:
|
|
if (event->code == KEY_ESC)
|
|
event->code = KEY_CAPSLOCK;
|
|
break;
|
|
case 2:
|
|
switch (event->code) {
|
|
case KEY_ESC:
|
|
event->code = KEY_GRAVE;
|
|
break;
|
|
case KEY_GRAVE:
|
|
event->code = KEY_CAPSLOCK;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
write_event(event);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int mode = 0, delay = 20000;
|
|
|
|
for (int opt; (opt = getopt(argc, argv, "ht:m:")) != -1;) {
|
|
switch (opt) {
|
|
case 'h':
|
|
return print_usage(stdout, argv[0]), EXIT_SUCCESS;
|
|
case 'm':
|
|
mode = atoi(optarg);
|
|
continue;
|
|
case 't':
|
|
delay = atoi(optarg);
|
|
continue;
|
|
}
|
|
|
|
return print_usage(stderr, argv[0]), EXIT_FAILURE;
|
|
}
|
|
|
|
struct input_event input;
|
|
enum { START, CAPSLOCK_HELD, CAPSLOCK_IS_CTRL } state = START;
|
|
|
|
setbuf(stdin, NULL), setbuf(stdout, NULL);
|
|
|
|
while (read_event(&input)) {
|
|
if (input.type == EV_MSC && input.code == MSC_SCAN)
|
|
continue;
|
|
|
|
if (input.type != EV_KEY && input.type != EV_REL &&
|
|
input.type != EV_ABS) {
|
|
write_event(&input);
|
|
continue;
|
|
}
|
|
|
|
switch (state) {
|
|
case START:
|
|
if (input.type == EV_KEY && input.code == KEY_CAPSLOCK &&
|
|
input.value)
|
|
state = CAPSLOCK_HELD;
|
|
else
|
|
write_event_with_mode(&input, mode);
|
|
break;
|
|
case CAPSLOCK_HELD:
|
|
if (input.type == EV_KEY && input.code == KEY_CAPSLOCK) {
|
|
if (input.value == 0) {
|
|
write_event(&esc_down);
|
|
write_event(&syn);
|
|
usleep(delay);
|
|
write_event(&esc_up);
|
|
state = START;
|
|
}
|
|
} else if ((input.type == EV_KEY && input.value == 1) ||
|
|
input.type == EV_REL || input.type == EV_ABS) {
|
|
write_event(&ctrl_down);
|
|
write_event(&syn);
|
|
usleep(delay);
|
|
write_event_with_mode(&input, mode);
|
|
state = CAPSLOCK_IS_CTRL;
|
|
} else
|
|
write_event_with_mode(&input, mode);
|
|
break;
|
|
case CAPSLOCK_IS_CTRL:
|
|
if (input.type == EV_KEY && input.code == KEY_CAPSLOCK) {
|
|
input.code = KEY_LEFTCTRL;
|
|
write_event(&input);
|
|
if (input.value == 0)
|
|
state = START;
|
|
} else
|
|
write_event_with_mode(&input, mode);
|
|
break;
|
|
}
|
|
}
|
|
}
|