From 35576743d13156dd0ccd9dbcbbd66b2cbca29c91 Mon Sep 17 00:00:00 2001 From: verdant Date: Sun, 17 May 2026 19:15:38 +0800 Subject: Add config parser and route --- config.c | 117 +++++++++++++++ config.h | 36 +++++ main.c | 490 ++++++++++++++++++++++++++++++++++++--------------------------- 3 files changed, 437 insertions(+), 206 deletions(-) create mode 100644 config.c create mode 100644 config.h diff --git a/config.c b/config.c new file mode 100644 index 0000000..2913a4e --- /dev/null +++ b/config.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "config.h" + +void fix_config_file_owner(const char *config_path) { + char *sudo_uid_str = getenv("SUDO_UID"); + char *sudo_gid_str = getenv("SUDO_GID"); + + if (sudo_uid_str && sudo_gid_str) { + uid_t original_uid = atoi(sudo_uid_str); + gid_t original_gid = atoi(sudo_gid_str); + + chown(config_path, original_uid, original_gid); + } +} + +struct config* prepare_config_file() { + struct config* cfg = malloc(sizeof(struct config)); + if (!cfg) { + return NULL; + } + + char* sudo_user = getenv("SUDO_USER"); + cfg->home = malloc(PATH_MAX); + if (!cfg->home) { + return NULL; + } + + snprintf(cfg->home, PATH_MAX, "/home/%s", sudo_user); + + cfg->config_file_path = malloc(strlen(cfg->home) + strlen("/.klrc")); + + snprintf(cfg->config_file_path, PATH_MAX, "%s/.klrc", cfg->home); + + if (access(cfg->config_file_path, F_OK) == -1) { + FILE* fp = fopen(cfg->config_file_path, "a"); + if (!fp) { + return NULL; + } + fputs(CONFIG_FILE_COMMENT, fp); + fix_config_file_owner(cfg->config_file_path); + printf("%s\n", cfg->config_file_path); + fclose(fp); + } + return cfg; +} + +struct config* parese_config(struct config* cfg) { + if (!cfg) { + return NULL; + } + + FILE* fp = fopen(cfg->config_file_path, "r"); + if (!fp) { + return NULL; + } + + + int line_count = 0; + char line[128]; + char key[64] = {0}; + char val[64] = {0}; + + while (fgets(line, sizeof(line), fp)) { + line_count++; + /* Skip comment and blank lines */ + if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') { + continue; + } + + int count = sscanf(line, "%s = %s", key, val); + if (count != 2) { + printf("Error at %s:%d\n invalid token: %s", cfg->config_file_path, line_count, line); + return NULL; + } + + if (CFG_COMPLETE(key, "device")) { + strncpy(cfg->device, val, sizeof(cfg->device) - 1); + cfg->device[sizeof(cfg->device) - 1] = '\0'; + } else if (CFG_COMPLETE(key, "enable_time")) { + if (CFG_COMPLETE(val, "true")) { + cfg->enable_time = true; + } else { + cfg->enable_time = false; + } + } else if (CFG_COMPLETE(key, "enable_key_counter")) { + if (CFG_COMPLETE(val, "true")) { + cfg->enable_keys_counter = true; + } else { + cfg->enable_keys_counter = false; + } + } + } + + fclose(fp); + + return cfg; +} + +struct config* config_init() { + struct config *cfg = prepare_config_file(); + if (!cfg) { + return NULL; + } + + if (parese_config(cfg) == NULL) { + return NULL; + } + + + return cfg; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..2911c70 --- /dev/null +++ b/config.h @@ -0,0 +1,36 @@ +#ifndef KC_CONFIG_H +#define KC_CONFIG_H + +#include +#include +#include + +#define PROGRAM_NAME "kl" +#define PROGRAM_FULL_NAME "Keyboard Listener (kl)" +#define PROGRAM_VERSION "0.1.1" +#define PROGRAM_AUTHORS "Verdant " + +#define CONFIG_FILE_COMMENT "# kl config file (v"PROGRAM_VERSION")\n" \ + "#\n" \ + "# Please see your README for details.\n" \ + "#\n" \ + "# This file was auto-generated because it was missing.\n" \ + +#define CFG_COMPLETE(key, val) ((strcmp((key), (val)) == 0) && ((val)[0] != '\0')) + +struct config { + char* home; + char* config_file_path; + char device[256]; + bool enable_time; + bool enable_keys_counter; +}; + +struct config* parese_config(struct config* cfg); + +struct config* prepare_config_file(); + +struct config* config_init(); + + +#endif diff --git a/main.c b/main.c index 8faac24..7c7cd51 100644 --- a/main.c +++ b/main.c @@ -5,234 +5,312 @@ #include #include #include +#include +#include #include - -#define PROGRAM_NAME "kl" +#include "config.h" static struct termios original_term; -const char *key_names[256] = {0}; +const char * key_names[256] = { + 0 +}; +static int repeat_counters[256] = { + 1 +}; +static int current_repeat_key = 0; +static int line_lock = -1; -void init_key_names() { - key_names[KEY_Q] = "Q"; - key_names[KEY_W] = "W"; - key_names[KEY_E] = "E"; - key_names[KEY_R] = "R"; - key_names[KEY_T] = "T"; - key_names[KEY_Y] = "Y"; - key_names[KEY_U] = "U"; - key_names[KEY_I] = "I"; - key_names[KEY_O] = "O"; - key_names[KEY_P] = "P"; - key_names[KEY_A] = "A"; - key_names[KEY_S] = "S"; - key_names[KEY_D] = "D"; - key_names[KEY_F] = "F"; - key_names[KEY_G] = "G"; - key_names[KEY_H] = "H"; - key_names[KEY_J] = "J"; - key_names[KEY_K] = "K"; - key_names[KEY_L] = "L"; - key_names[KEY_Z] = "Z"; - key_names[KEY_X] = "X"; - key_names[KEY_C] = "C"; - key_names[KEY_V] = "V"; - key_names[KEY_B] = "B"; - key_names[KEY_N] = "N"; - key_names[KEY_M] = "M"; - - key_names[KEY_1] = "1"; - key_names[KEY_2] = "2"; - key_names[KEY_3] = "3"; - key_names[KEY_4] = "4"; - key_names[KEY_5] = "5"; - key_names[KEY_6] = "6"; - key_names[KEY_7] = "7"; - key_names[KEY_8] = "8"; - key_names[KEY_9] = "9"; - key_names[KEY_0] = "0"; - - key_names[KEY_MINUS] = "-"; - key_names[KEY_EQUAL] = "="; - key_names[KEY_LEFTBRACE] = "["; - key_names[KEY_RIGHTBRACE] = "]"; - key_names[KEY_SEMICOLON] = ";"; - key_names[KEY_APOSTROPHE] = "'"; - key_names[KEY_GRAVE] = "`"; - key_names[KEY_BACKSLASH] = "\\"; - key_names[KEY_COMMA] = ","; - key_names[KEY_DOT] = "."; - key_names[KEY_SLASH] = "/"; - key_names[KEY_SPACE] = ""; - - key_names[KEY_ESC] = ""; - key_names[KEY_BACKSPACE] = ""; - key_names[KEY_TAB] = ""; - key_names[KEY_ENTER] = ""; - key_names[KEY_LEFTSHIFT] = ""; - key_names[KEY_RIGHTSHIFT] = ""; - key_names[KEY_LEFTCTRL] = ""; - key_names[KEY_RIGHTCTRL] = ""; - key_names[KEY_LEFTALT] = ""; - key_names[KEY_RIGHTALT] = ""; - key_names[KEY_LEFTMETA] = ""; - key_names[KEY_RIGHTMETA] = ""; - key_names[KEY_CAPSLOCK] = ""; - key_names[KEY_NUMLOCK] = ""; - key_names[KEY_SCROLLLOCK] = ""; - - key_names[KEY_F1] = ""; - key_names[KEY_F2] = ""; - key_names[KEY_F3] = ""; - key_names[KEY_F4] = ""; - key_names[KEY_F5] = ""; - key_names[KEY_F6] = ""; - key_names[KEY_F7] = ""; - key_names[KEY_F8] = ""; - key_names[KEY_F9] = ""; - key_names[KEY_F10] = ""; - key_names[KEY_F11] = ""; - key_names[KEY_F12] = ""; - key_names[KEY_F13] = ""; - key_names[KEY_F14] = ""; - key_names[KEY_F15] = ""; - key_names[KEY_F16] = ""; - key_names[KEY_F17] = ""; - key_names[KEY_F18] = ""; - key_names[KEY_F19] = ""; - key_names[KEY_F20] = ""; - key_names[KEY_F21] = ""; - key_names[KEY_F22] = ""; - key_names[KEY_F23] = ""; - key_names[KEY_F24] = ""; - - key_names[KEY_KP0] = ""; - key_names[KEY_KP1] = ""; - key_names[KEY_KP2] = ""; - key_names[KEY_KP3] = ""; - key_names[KEY_KP4] = ""; - key_names[KEY_KP5] = ""; - key_names[KEY_KP6] = ""; - key_names[KEY_KP7] = ""; - key_names[KEY_KP8] = ""; - key_names[KEY_KP9] = ""; - key_names[KEY_KPDOT] = ""; - key_names[KEY_KPCOMMA] = ""; - key_names[KEY_KPENTER] = ""; - key_names[KEY_KPMINUS] = ""; - key_names[KEY_KPPLUS] = ""; - key_names[KEY_KPSLASH] = ""; - key_names[KEY_KPASTERISK] = ""; - key_names[KEY_KPEQUAL] = ""; - - key_names[KEY_UP] = ""; - key_names[KEY_DOWN] = ""; - key_names[KEY_LEFT] = ""; - key_names[KEY_RIGHT] = ""; - key_names[KEY_HOME] = ""; - key_names[KEY_END] = ""; - key_names[KEY_PAGEUP] = ""; - key_names[KEY_PAGEDOWN] = ""; - key_names[KEY_INSERT] = ""; - key_names[KEY_DELETE] = ""; - - key_names[KEY_MUTE] = ""; - key_names[KEY_VOLUMEDOWN] = ""; - key_names[KEY_VOLUMEUP] = ""; - key_names[KEY_POWER] = ""; - key_names[KEY_SLEEP] = ""; - key_names[KEY_WAKEUP] = ""; - key_names[KEY_PLAY] = ""; - key_names[KEY_PAUSE] = ""; - key_names[KEY_PLAYPAUSE] = ""; - key_names[KEY_STOPCD] = ""; - key_names[KEY_PREVIOUSSONG] = ""; - key_names[KEY_NEXTSONG] = ""; - key_names[KEY_REWIND] = ""; - key_names[KEY_FASTFORWARD] = ""; - key_names[KEY_RECORD] = ""; - key_names[KEY_EJECTCD] = ""; - - key_names[KEY_SYSRQ] = ""; - key_names[KEY_PAUSE] = ""; - key_names[KEY_MENU] = ""; - key_names[KEY_CALC] = ""; - key_names[KEY_MAIL] = ""; - key_names[KEY_WWW] = ""; - key_names[KEY_SEARCH] = ""; - key_names[KEY_BACK] = ""; - key_names[KEY_FORWARD] = ""; - key_names[KEY_REFRESH] = ""; - key_names[KEY_STOP] = ""; - key_names[KEY_COPY] = ""; - key_names[KEY_CUT] = ""; - key_names[KEY_PASTE] = ""; - key_names[KEY_UNDO] = ""; - key_names[KEY_REDO] = ""; - key_names[KEY_FIND] = ""; - key_names[KEY_HELP] = ""; - key_names[KEY_FILE] = ""; - key_names[KEY_SAVE] = ""; - key_names[KEY_NEW] = ""; - key_names[KEY_OPEN] = ""; - key_names[KEY_CLOSE] = ""; - key_names[KEY_EXIT] = ""; - key_names[KEY_PRINT] = ""; - key_names[KEY_BRIGHTNESSDOWN] = ""; - key_names[KEY_BRIGHTNESSUP] = ""; - key_names[KEY_DISPLAY_OFF] = ""; - key_names[KEY_MICMUTE] = ""; - key_names[KEY_WLAN] = ""; - key_names[KEY_BLUETOOTH] = ""; -} +enum KEY_STATUS { + KEY_RELEASE = 0, + KEY_PRESS = 1, + KEY_REPEAT = 2 +}; void usage() { - printf("Usage: %s .\n", PROGRAM_NAME); + printf("Usage: %s .\n", PROGRAM_NAME); +} + +void version() { + printf("%s (v%s)\n", PROGRAM_FULL_NAME, PROGRAM_VERSION); + printf("%s\n", PROGRAM_AUTHORS); + +} + +void init_key_names() { + key_names[KEY_RESERVED] = "KEY_RESERVED"; + key_names[KEY_Q] = "Q"; + key_names[KEY_W] = "W"; + key_names[KEY_E] = "E"; + key_names[KEY_R] = "R"; + key_names[KEY_T] = "T"; + key_names[KEY_Y] = "Y"; + key_names[KEY_U] = "U"; + key_names[KEY_I] = "I"; + key_names[KEY_O] = "O"; + key_names[KEY_P] = "P"; + key_names[KEY_A] = "A"; + key_names[KEY_S] = "S"; + key_names[KEY_D] = "D"; + key_names[KEY_F] = "F"; + key_names[KEY_G] = "G"; + key_names[KEY_H] = "H"; + key_names[KEY_J] = "J"; + key_names[KEY_K] = "K"; + key_names[KEY_L] = "L"; + key_names[KEY_Z] = "Z"; + key_names[KEY_X] = "X"; + key_names[KEY_C] = "C"; + key_names[KEY_V] = "V"; + key_names[KEY_B] = "B"; + key_names[KEY_N] = "N"; + key_names[KEY_M] = "M"; + + key_names[KEY_1] = "1"; + key_names[KEY_2] = "2"; + key_names[KEY_3] = "3"; + key_names[KEY_4] = "4"; + key_names[KEY_5] = "5"; + key_names[KEY_6] = "6"; + key_names[KEY_7] = "7"; + key_names[KEY_8] = "8"; + key_names[KEY_9] = "9"; + key_names[KEY_0] = "0"; + + key_names[KEY_MINUS] = "-"; + key_names[KEY_EQUAL] = "="; + key_names[KEY_LEFTBRACE] = "["; + key_names[KEY_RIGHTBRACE] = "]"; + key_names[KEY_SEMICOLON] = ";"; + key_names[KEY_APOSTROPHE] = "'"; + key_names[KEY_GRAVE] = "`"; + key_names[KEY_BACKSLASH] = "\\"; + key_names[KEY_COMMA] = ","; + key_names[KEY_DOT] = "."; + key_names[KEY_SLASH] = "/"; + key_names[KEY_SPACE] = ""; + + key_names[KEY_ESC] = ""; + key_names[KEY_BACKSPACE] = ""; + key_names[KEY_TAB] = ""; + key_names[KEY_ENTER] = ""; + key_names[KEY_LEFTSHIFT] = ""; + key_names[KEY_RIGHTSHIFT] = ""; + key_names[KEY_LEFTCTRL] = ""; + key_names[KEY_RIGHTCTRL] = ""; + key_names[KEY_LEFTALT] = ""; + key_names[KEY_RIGHTALT] = ""; + key_names[KEY_LEFTMETA] = ""; + key_names[KEY_RIGHTMETA] = ""; + key_names[KEY_CAPSLOCK] = ""; + key_names[KEY_NUMLOCK] = ""; + key_names[KEY_SCROLLLOCK] = ""; + + key_names[KEY_F1] = ""; + key_names[KEY_F2] = ""; + key_names[KEY_F3] = ""; + key_names[KEY_F4] = ""; + key_names[KEY_F5] = ""; + key_names[KEY_F6] = ""; + key_names[KEY_F7] = ""; + key_names[KEY_F8] = ""; + key_names[KEY_F9] = ""; + key_names[KEY_F10] = ""; + key_names[KEY_F11] = ""; + key_names[KEY_F12] = ""; + key_names[KEY_F13] = ""; + key_names[KEY_F14] = ""; + key_names[KEY_F15] = ""; + key_names[KEY_F16] = ""; + key_names[KEY_F17] = ""; + key_names[KEY_F18] = ""; + key_names[KEY_F19] = ""; + key_names[KEY_F20] = ""; + key_names[KEY_F21] = ""; + key_names[KEY_F22] = ""; + key_names[KEY_F23] = ""; + key_names[KEY_F24] = ""; + + key_names[KEY_KP0] = ""; + key_names[KEY_KP1] = ""; + key_names[KEY_KP2] = ""; + key_names[KEY_KP3] = ""; + key_names[KEY_KP4] = ""; + key_names[KEY_KP5] = ""; + key_names[KEY_KP6] = ""; + key_names[KEY_KP7] = ""; + key_names[KEY_KP8] = ""; + key_names[KEY_KP9] = ""; + key_names[KEY_KPDOT] = ""; + key_names[KEY_KPCOMMA] = ""; + key_names[KEY_KPENTER] = ""; + key_names[KEY_KPMINUS] = ""; + key_names[KEY_KPPLUS] = ""; + key_names[KEY_KPSLASH] = ""; + key_names[KEY_KPASTERISK] = ""; + key_names[KEY_KPEQUAL] = ""; + + key_names[KEY_UP] = ""; + key_names[KEY_DOWN] = ""; + key_names[KEY_LEFT] = ""; + key_names[KEY_RIGHT] = ""; + key_names[KEY_HOME] = ""; + key_names[KEY_END] = ""; + key_names[KEY_PAGEUP] = ""; + key_names[KEY_PAGEDOWN] = ""; + key_names[KEY_INSERT] = ""; + key_names[KEY_DELETE] = ""; + + key_names[KEY_MUTE] = ""; + key_names[KEY_VOLUMEDOWN] = ""; + key_names[KEY_VOLUMEUP] = ""; + key_names[KEY_POWER] = ""; + key_names[KEY_SLEEP] = ""; + key_names[KEY_WAKEUP] = ""; + key_names[KEY_PLAY] = ""; + key_names[KEY_PAUSE] = ""; + key_names[KEY_PLAYPAUSE] = ""; + key_names[KEY_STOPCD] = ""; + key_names[KEY_PREVIOUSSONG] = ""; + key_names[KEY_NEXTSONG] = ""; + key_names[KEY_REWIND] = ""; + key_names[KEY_FASTFORWARD] = ""; + key_names[KEY_RECORD] = ""; + key_names[KEY_EJECTCD] = ""; + + key_names[KEY_SYSRQ] = ""; + key_names[KEY_PAUSE] = ""; + key_names[KEY_MENU] = ""; + key_names[KEY_CALC] = ""; + key_names[KEY_MAIL] = ""; + key_names[KEY_WWW] = ""; + key_names[KEY_SEARCH] = ""; + key_names[KEY_BACK] = ""; + key_names[KEY_FORWARD] = ""; + key_names[KEY_REFRESH] = ""; + key_names[KEY_STOP] = ""; + key_names[KEY_COPY] = ""; + key_names[KEY_CUT] = ""; + key_names[KEY_PASTE] = ""; + key_names[KEY_UNDO] = ""; + key_names[KEY_REDO] = ""; + key_names[KEY_FIND] = ""; + key_names[KEY_HELP] = ""; + key_names[KEY_FILE] = ""; + key_names[KEY_SAVE] = ""; + key_names[KEY_NEW] = ""; + key_names[KEY_OPEN] = ""; + key_names[KEY_CLOSE] = ""; + key_names[KEY_EXIT] = ""; + key_names[KEY_PRINT] = ""; + key_names[KEY_BRIGHTNESSDOWN] = ""; + key_names[KEY_BRIGHTNESSUP] = ""; + key_names[KEY_DISPLAY_OFF] = ""; + key_names[KEY_MICMUTE] = ""; + key_names[KEY_WLAN] = ""; + key_names[KEY_BLUETOOTH] = ""; } void disable_term_echo() { - struct termios new_setting, init_setting; - tcgetattr(0, &init_setting); - new_setting = init_setting; + struct termios new_setting, init_setting; + tcgetattr(0, & original_term); + new_setting = original_term; - new_setting.c_lflag&=~ECHO; - tcsetattr(0, TCSANOW, &new_setting); + new_setting.c_lflag &= ~ECHO; + tcsetattr(0, TCSANOW, & new_setting); } void enable_term_echo() { - tcsetattr(0, TCSANOW, &original_term); + tcsetattr(0, TCSANOW, & original_term); +} + +void print_pure(struct input_event * ie) { + if (repeat_counters[ie -> code] != 1) { + printf("%-15s x%-13d\r", key_names[ie -> code], repeat_counters[ie -> code]); + } else { + printf("\r%s", key_names[ie -> code]); + } + + fflush(stdout); } -int main(int argc, char** argv) { - if (argc != 2 || strcmp(argv[1], "--help") == 0) { - usage(); - return -1; - } +void print_with_time(struct input_event * ie) { + struct tm * tm_info; + char time_buf[64]; + time_t sec = (time_t) ie -> input_event_sec; + tm_info = localtime( & sec); + strftime(time_buf, sizeof(time_buf), "%F %T", tm_info); + + if (repeat_counters[ie -> code] != 1) { + printf("%s.%06ld: %s x%d\n", time_buf, (long) ie -> time.tv_usec, key_names[ie -> code], repeat_counters[ie -> code]); + } else { + printf("%s.%06ld: %s\n", time_buf, (long) ie -> time.tv_usec, key_names[ie -> code]); + + } + + return; +} + +int main(int argc, char ** argv) { + if (argv[1] != NULL && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) { + usage(); + return -1; + } else if (argv[1] != NULL && (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v")) == 0) { + version(); + return -1; + } + + setvbuf(stdout, NULL, _IONBF, 0); + disable_term_echo(); + + struct config * cfg = config_init(); + if (!cfg) { + return -1; + } + + printf("Device: %s\n", cfg -> device); + + struct input_event ie; + int fd = open(cfg -> device, O_RDONLY); + + if (fd == -1 && errno == EACCES) { + printf("Permission denied: please run %s with sudo.\n", PROGRAM_NAME); + return -1; + } + + if (fd == -1 && errno == ENOENT) { + printf("No such file or directory: %s\n", cfg -> device); + return -1; - disable_term_echo(); + } - struct input_event ie; - int fd = open(argv[1], O_RDONLY); + init_key_names(); - if (fd == -1 && errno == EACCES) { - printf("Permission denied: please run %s with sudo.\n", PROGRAM_NAME); - return -1; - } + void( * fp)(struct input_event * ie) = (cfg -> enable_time == true) ? print_with_time : print_pure; - init_key_names(); + while (1) { + read(fd, & ie, sizeof(ie)); + if (ie.type == EV_KEY) { - while (1) { - read(fd, &ie, sizeof(ie)); + if (ie.value == KEY_PRESS) { + if (line_lock != -1 && line_lock != ie.code && !cfg -> enable_time) printf("\n"); + repeat_counters[ie.code] = 1; + line_lock = ie.code; + fp( & ie); + } - if (ie.type == EV_KEY && ie.value == 2) { - continue; - } + if (ie.value == KEY_REPEAT) { + line_lock = ie.code; + repeat_counters[ie.code]++; + fp( & ie); + } - if (key_names[ie.code] != NULL && ie.value == 1) { - printf("%s\n", key_names[ie.code]); - } + if (ie.value == KEY_RELEASE) { + repeat_counters[ie.code] = 1; + } + } - } + } - enable_term_echo(); - return 0; + enable_term_echo(); + return 0; } -- cgit v1.2.3