// // Copyright (C) 2021 Andy Pugh // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // #include "rtapi.h" #include "hal.h" #include #include #include #include #include #include #define MAX_NAME 12 static int comp_id; typedef struct{ hal_u32_t *keycode; hal_s32_t *current_event; hal_bit_t *init; hal_bit_t **trigger; hal_u32_t *event; } sendkeys_hal; typedef struct { int num_codes; int num_events; int num_triggers; bool inited; int fd; bool *prev; hal_u32_t oldcode; } sendkeys_param; typedef struct { sendkeys_hal *hal; sendkeys_param *param; int num_insts; } sendkeys ; static sendkeys *me; void emit(int fd, int type, int code, int val) { struct input_event ie; ie.type = type; ie.code = code; ie.value = val; if (write(fd, &ie, sizeof(ie)) != sizeof(ie)) { perror("write(input_event)"); } } static void exit_handler(int sig) { printf("sendkeys: exiting\n"); exit(0); } static void call_hal_exit(void) { hal_exit(comp_id); } int init(int argc, char* argv[]){ int *codes; int *pins; int i, j; comp_id = hal_init("sendkeys"); if (comp_id < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys: ERROR: hal_init() failed\n"); return -1; } me = hal_malloc(sizeof(sendkeys)); codes = malloc(sizeof(int)); pins = malloc(sizeof(int)); // This is all because RTAPI_MP_ARRAY_** macros do not appear to work in userspace for (i = 1; i < argc; i++){ static int index = -1; static int type = -1; void *v1, *v2; int ptr = 0; bool shift = 0; if (strncmp(argv[i], "config=", 7) == 0) type = 1, ptr = 6; if (strncmp(argv[i], "names=", 6) == 0) type = 2, ptr = 5; switch (type) { case 1: // config while (argv[i][ptr]){ switch (argv[i][ptr]){ case '=': case ' ': case ',': index++; v1 = realloc(codes, (index + 1) * sizeof(int)); v2 = realloc(pins, (index + 1) * sizeof(int)); if (!v1 || !v2) { free(v1 ? v1 : codes); free(v2 ? v2 : pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.keycode error\n"); return -ENOMEM; } else { codes=v1; pins=v2; } codes[index] = 0; pins[index] = 0; shift = 0; break; case 's': case 'S': shift = 0; break; case 't': case 'T': shift = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (shift){ pins[index] = pins[index] * 10 + argv[i][ptr] - '0'; } else { codes[index] = codes[index] * 10 + argv[i][ptr] - '0'; } break; } ptr++;; } me->num_insts = index + 1; break; case 2: // Parsing of "names" would be here, but is not enabled yet while (argv[i][ptr]){ ptr++; } break; } } me->hal = (sendkeys_hal*)hal_malloc(me->num_insts * sizeof(sendkeys_hal)); me->param = malloc(me->num_insts * sizeof(sendkeys_param)); for (i = 0; i < me->num_insts; i++){ sendkeys_hal* hal = &(me->hal[i]); sendkeys_param* param = &(me->param[i]); if (hal_pin_u32_newf(HAL_IN, &(hal->keycode), comp_id, "sendkeys.%i.keycode", i) < 0) { free(codes); free(pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.keycode error\n"); return -ENOMEM;} if (hal_pin_s32_newf(HAL_OUT, &(hal->current_event), comp_id, "sendkeys.%i.current-event", i) < 0) { free(codes); free(pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.current-event error\n"); return -ENOMEM;} if (hal_pin_bit_newf(HAL_IN, &(hal->init), comp_id, "sendkeys.%i.init", i) < 0) { free(codes); free(pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.init error\n"); return -ENOMEM;} // event params rtapi_print_msg(RTAPI_MSG_DBG, "instance %i events %i triggers %i\n", i, codes[i], pins[i]); param->num_triggers = pins[i]; param->num_codes = codes[i]; param->num_events = codes[i] + pins[i]; hal->event = hal_malloc(param->num_events * sizeof(hal_u32_t*)); for (j = 0; j < param->num_codes; j++){ if (hal_param_u32_newf(HAL_RW, &(hal->event[j]), comp_id, "sendkeys.%i.scan-event-%02i", i, j) < 0) { free(codes); free(pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.scan-event-%02i error\n", j); return -ENOMEM;} } for (j = 0; j < param->num_triggers; j++){ if (hal_param_u32_newf(HAL_RW, &(hal->event[j + param->num_codes]), comp_id, "sendkeys.%i.pin-event-%02i", i, j) < 0) { free(codes); free(pins); rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.pin-event-%02i error\n", j); return -ENOMEM;} } // trigger pins hal->trigger = hal_malloc(param->num_triggers * sizeof(hal_bit_t*)); param->prev = malloc(param->num_triggers * sizeof(hal_bit_t)); for (j = 0; j < param->num_triggers; j++){ if (hal_pin_bit_newf(HAL_IN, &(hal->trigger[j]), comp_id, "sendkeys.%i.trigger-%02i", i, j) < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "sendkeys.N.trigger-%02i error\n",j); free(codes); free(pins); return -ENOMEM;} param->prev[j] = 0; } } free(codes); free(pins); return 0; } int main(int argc, char* argv[]) { int i, j; signal(SIGINT, exit_handler); signal(SIGTERM, exit_handler); atexit(call_hal_exit); //parse configs and create pins init(argc, argv); hal_ready(comp_id); while (1) { for (i = 0; i < me->num_insts; i++){ sendkeys_hal* hal = &(me->hal[i]); sendkeys_param* param = &(me->param[i]); if ((*hal->keycode & 0xC0) == 0x80){ *hal->current_event = (*hal->keycode & 0x3f); } else { *hal->current_event = -1; } // The event codes are written to pins in HAL after the // component is loaded. // We need to set up the keyboard based on the events selected // so there is a secondary init here. if (! *hal->init) continue; if (*hal->init && ! param->inited) { param->fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); if (param->fd < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "Cannot open /dev/uinput. Suggest chmod 666 /dev/uinput\n"); abort(); } ioctl(param->fd, UI_SET_EVBIT, EV_KEY); for (j = 0; j < param->num_events; j++){ if (hal->event[j] > 0 && hal->event[j] < KEY_MAX){ ioctl(param->fd, UI_SET_KEYBIT, hal->event[j]); rtapi_print("SET_EVBIT %i\n", hal->event[j]); } } struct uinput_user_dev uidev; memset(&uidev, 0, sizeof(uidev)); strcpy(uidev.name, "linuxcnc-hal"); uidev.id.bustype = BUS_USB; uidev.id.vendor = 0x1; uidev.id.product = 0x1; uidev.id.version = 1; if (write(param->fd, &uidev, sizeof(uidev)) != sizeof(uidev)) { perror("write(uinput_user_dev)"); abort(); } if (ioctl(param->fd, UI_DEV_CREATE) < 0) { perror("ioctl(UI_DEV_CREATE)"); abort(); } param->inited = 1; } if (*hal->keycode != param->oldcode) { /* Key press, report the event, send key release, and report again*/ if ((*hal->keycode & 0x3F) > param->num_events) continue; if (hal->event[*hal->keycode & 0x3F] == 0) continue; if ((*hal->keycode & 0xC0) == 0xC0){ // keydown emit(param->fd, EV_KEY, hal->event[*hal->keycode & 0x3F], 1); emit(param->fd, EV_SYN, SYN_REPORT, 0); } else if ((*hal->keycode & 0xC0) == 0x80){ // keyup emit(param->fd, EV_KEY, hal->event[*hal->keycode & 0x3F], 0); emit(param->fd, EV_SYN, SYN_REPORT, 0); } param->oldcode = *hal->keycode; } for (j = 0; j < param->num_triggers; j++){ if (param->prev[j] != *hal->trigger[j]){ if (*hal->trigger[j]){ // keydown emit(param->fd, EV_KEY, hal->event[param->num_codes + j], 1); emit(param->fd, EV_SYN, SYN_REPORT, 0); } else { // keyup emit(param->fd, EV_KEY, hal->event[param->num_codes + j], 0); emit(param->fd, EV_SYN, SYN_REPORT, 0); } param->prev[j] = *hal->trigger[j]; } } } } }