static int keyboard_new(struct dirent *ent, struct xkb_keymap *keymap, struct keyboard **out) { int ret; char *path; int fd; struct xkb_state *state; struct keyboard *kbd; ret = asprintf(&path, "/dev/input/%s", ent->d_name); if (ret < 0) return -ENOMEM; fd = open(path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); if (fd < 0) { ret = -errno; goto err_path; } if (!is_keyboard(fd)) { /* Dummy "skip this device" value. */ ret = -ENOTSUP; goto err_fd; } state = xkb_state_new(keymap); if (!state) { fprintf(stderr, "Couldn't create xkb state for %s\n", path); ret = -EFAULT; goto err_fd; } kbd = calloc(1, sizeof(*kbd)); if (!kbd) { ret = -ENOMEM; goto err_state; } kbd->path = path; kbd->fd = fd; kbd->state = state; *out = kbd; return 0; err_state: xkb_state_unref(state); err_fd: close(fd); err_path: free(path); return ret; }
int main(int argc, char **argv) { struct option long_options[] = { {"keyboard", required_argument, NULL, 'k'}, {"mouse", required_argument, NULL, 'm'}, {"help", no_argument, NULL, 'h'}, }; char *keyboard_cmd = NULL; char *mouse_cmd = NULL; struct udev *udev; struct udev_monitor *monitor; struct pollfd pollfd; int status = EXIT_FAILURE; int ret; if (argc > 0) progname = argv[0]; for (;;) { int c; c = getopt_long(argc, argv, "kmh", long_options, NULL); if (c == -1) break; switch (c) { case 'k': keyboard_cmd = strdup(optarg); if (!keyboard_cmd) { perror("strdup"); goto out; } break; case 'm': mouse_cmd = strdup(optarg); if (!mouse_cmd) { perror("strdup"); goto out; } break; case 'h': free(keyboard_cmd); free(mouse_cmd); usage(false); default: free(keyboard_cmd); free(mouse_cmd); usage(true); } } if (optind != argc || (!keyboard_cmd && !mouse_cmd)) usage(true); if (keyboard_cmd) run_cmd(keyboard_cmd, "keyboard"); if (mouse_cmd) run_cmd(mouse_cmd, "mouse"); udev = udev_new(); if (!udev) { fprintf(stderr, "failed to allocate a new udev context\n"); goto out; } monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!monitor) { fprintf(stderr, "failed to allocate udev monitor\n"); goto out_udev; } ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", NULL); if (ret != 0) { fprintf(stderr, "failed to add udev subsystem filter\n"); goto out_udev_monitor; } ret = udev_monitor_enable_receiving(monitor); if (ret != 0) { fprintf(stderr, "failed to enable udev monitor\n"); goto out_udev_monitor; } pollfd.fd = udev_monitor_get_fd(monitor); pollfd.events = POLLIN; for (;;) { struct udev_device *device; bool run_keyboard = false; bool run_mouse = false; pollfd.revents = 0; ret = poll(&pollfd, 1, -1); if (ret == -1) { perror("poll"); goto out_udev_monitor; } if (!(pollfd.revents & POLLIN)) continue; while ((device = udev_monitor_receive_device(monitor))) { if (strcmp(udev_device_get_action(device), "add") == 0) { if (is_keyboard(device)) run_keyboard = true; if (is_mouse(device)) run_mouse = true; } udev_device_unref(device); } if (run_keyboard && keyboard_cmd) run_cmd(keyboard_cmd, "keyboard"); if (run_mouse && mouse_cmd) run_cmd(mouse_cmd, "mouse"); } status = EXIT_SUCCESS; out_udev_monitor: udev_monitor_unref(monitor); out_udev: udev_unref(udev); out: free(keyboard_cmd); free(mouse_cmd); return status; }
int main() { struct termios old_term, new_term; int kb = -1; /* keyboard file descriptor */ char *files_to_try[] = {"/dev/tty", "/dev/console", NULL}; int old_mode = -1; int i; /* First we need to find a file descriptor that represents the system's keyboard. This should be /dev/tty, /dev/console, stdin, stdout, or stderr. We'll try them in that order. If none are acceptable, we're probably not being run from a VT. */ for (i = 0; files_to_try[i] != NULL; i++) { /* Try to open the file. */ kb = open(files_to_try[i], O_RDONLY); if (kb < 0) continue; /* See if this is valid for our purposes. */ if (is_keyboard(kb)) { printf("Using keyboard on %s.\n", files_to_try[i]); break; } close(kb); } /* If those didn't work, not all is lost. We can try the 3 standard file descriptors, in hopes that one of them might point to a console. This is not especially likely. */ if (files_to_try[i] == NULL) { for (kb = 0; kb < 3; kb++) { if (is_keyboard(i)) break; } printf("Unable to find a file descriptor associated with the "\ "keyboard.\n" \ "Perhaps you're not using a virtual terminal?\n"); return 1; } /* Find the keyboard's current mode so we can restore it later. */ if (ioctl(kb, KDGKBMODE, &old_mode) != 0) { printf("Unable to query keyboard mode.\n"); goto error; } /* Adjust the terminal's settings. In particular, disable echoing, signal generation, and line buffering. Any of these could cause trouble. Save the old settings first. */ if (tcgetattr(kb, &old_term) != 0) { printf("Unable to query terminal settings.\n"); goto error; } new_term = old_term; new_term.c_iflag = 0; new_term.c_lflag &= ~(ECHO | ICANON | ISIG); /* TCSAFLUSH discards unread input before making the change. A good idea. */ if (tcsetattr(kb, TCSAFLUSH, &new_term) != 0) { printf("Unable to change terminal settings.\n"); } /* Put the keyboard in mediumraw mode. */ if (ioctl(kb, KDSKBMODE, K_MEDIUMRAW) != 0) { printf("Unable to set mediumraw mode.\n"); goto error; } printf("Reading keycodes. Press Escape (keycode 1) to exit.\n"); for (;;) { unsigned char data; if (read(kb, &data, 1) < 1) { printf("Unable to read data. Trying to exit nicely.\n"); goto error; } /* Print the keycode. The top bit is the pressed/released flag, and the lower seven are the keycode. */ printf("%s: %2Xh (%i)\n", (data & 0x80) ? "Released" : " Pressed", (unsigned int)data & 0x7F, (unsigned int)data & 0x7F); if ((data & 0x7F) == 1) { printf("Escape pressed.\n"); break; } } /* Shut down nicely. */ printf("Exiting normally.\n"); ioctl(kb, KDSKBMODE, old_mode); tcsetattr(kb, 0, &old_term); if (kb > 3) close(kb); return 0; error: printf("Cleaning up.\n"); fflush(stdout); /* Restore the previous mode. Users hate it when they can't use the keyboard. */ if (old_mode != -1) { ioctl(kb, KDSKBMODE, old_mode); tcsetattr(kb, 0, &old_term); } /* Only bother closing the keyboard fd if it's not stdin, stdout, or stderr. */ if (kb > 3) close(kb); return 1; }