#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <getopt.h>
#include <limits.h>
#include <locale.h>
+#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <sys/epoll.h>
#include <linux/input.h>
#include "xkbcommon/xkbcommon.h"
static bool with_compose;
static enum xkb_consumed_mode consumed_mode = XKB_CONSUMED_MODE_XKB;
+#define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__"
#define NLONGS(n) (((n) + LONG_BIT - 1) / LONG_BIT)
static bool
static int
loop(struct keyboard *kbds)
{
- int i, ret = 1;
- int epfd = -1;
+ int ret = -1;
struct keyboard *kbd;
- struct epoll_event ev;
- struct epoll_event evs[16];
+ nfds_t nfds, i;
+ struct pollfd *fds = NULL;
- epfd = epoll_create1(0);
- if (epfd < 0) {
- fprintf(stderr, "Couldn't create epoll instance: %s\n",
- strerror(errno));
+ for (kbd = kbds, nfds = 0; kbd; kbd = kbd->next, nfds++) {}
+ fds = calloc(nfds, sizeof(*fds));
+ if (fds == NULL) {
+ fprintf(stderr, "Out of memory");
goto out;
}
- for (kbd = kbds; kbd; kbd = kbd->next) {
- memset(&ev, 0, sizeof(ev));
- ev.events = EPOLLIN;
- ev.data.ptr = kbd;
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, kbd->fd, &ev);
- if (ret) {
- fprintf(stderr, "Couldn't add %s to epoll: %s\n",
- kbd->path, strerror(errno));
- goto out;
- }
+ for (i = 0, kbd = kbds; kbd; kbd = kbd->next, i++) {
+ fds[i].fd = kbd->fd;
+ fds[i].events = POLLIN;
}
while (!terminate) {
- ret = epoll_wait(epfd, evs, 16, -1);
+ ret = poll(fds, nfds, -1);
if (ret < 0) {
if (errno == EINTR)
continue;
goto out;
}
- for (i = 0; i < ret; i++) {
- kbd = evs[i].data.ptr;
- ret = read_keyboard(kbd);
- if (ret) {
- goto out;
+ for (i = 0, kbd = kbds; kbd; kbd = kbd->next, i++) {
+ if (fds[i].revents != 0) {
+ ret = read_keyboard(kbd);
+ if (ret) {
+ goto out;
+ }
}
}
}
ret = 0;
out:
- close(epfd);
+ free(fds);
return ret;
}
terminate = true;
}
+static void
+usage(FILE *fp, char *progname)
+{
+ fprintf(fp, "Usage: %s [--include=<path>] [--include-defaults] "
+ "[--rules=<rules>] [--model=<model>] [--layout=<layout>] "
+ "[--variant=<variant>] [--options=<options>]\n",
+ progname);
+ fprintf(fp, " or: %s --keymap <path to keymap file>\n",
+ progname);
+ fprintf(fp, "For both:\n"
+ " --report-state-changes (report changes to the state)\n"
+ " --enable-compose (enable Compose)\n"
+ " --consumed-mode={xkb|gtk} (select the consumed modifiers mode, default: xkb)\n"
+ " --without-x11-offset (don't add X11 keycode offset)\n"
+ );
+}
+
int
main(int argc, char *argv[])
{
int ret = EXIT_FAILURE;
- int opt;
struct keyboard *kbds;
struct xkb_context *ctx = NULL;
struct xkb_keymap *keymap = NULL;
struct xkb_compose_table *compose_table = NULL;
+ const char *includes[64];
+ size_t num_includes = 0;
const char *rules = NULL;
const char *model = NULL;
const char *layout = NULL;
const char *keymap_path = NULL;
const char *locale;
struct sigaction act;
+ enum options {
+ OPT_INCLUDE,
+ OPT_INCLUDE_DEFAULTS,
+ OPT_RULES,
+ OPT_MODEL,
+ OPT_LAYOUT,
+ OPT_VARIANT,
+ OPT_OPTION,
+ OPT_KEYMAP,
+ OPT_WITHOUT_X11_OFFSET,
+ OPT_CONSUMED_MODE,
+ OPT_COMPOSE,
+ OPT_REPORT_STATE,
+ };
+ static struct option opts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"include", required_argument, 0, OPT_INCLUDE},
+ {"include-defaults", no_argument, 0, OPT_INCLUDE_DEFAULTS},
+ {"rules", required_argument, 0, OPT_RULES},
+ {"model", required_argument, 0, OPT_MODEL},
+ {"layout", required_argument, 0, OPT_LAYOUT},
+ {"variant", required_argument, 0, OPT_VARIANT},
+ {"options", required_argument, 0, OPT_OPTION},
+ {"keymap", required_argument, 0, OPT_KEYMAP},
+ {"consumed-mode", required_argument, 0, OPT_CONSUMED_MODE},
+ {"enable-compose", no_argument, 0, OPT_COMPOSE},
+ {"report-state-changes", no_argument, 0, OPT_REPORT_STATE},
+ {"without-x11-offset", no_argument, 0, OPT_WITHOUT_X11_OFFSET},
+ {0, 0, 0, 0},
+ };
setlocale(LC_ALL, "");
- while ((opt = getopt(argc, argv, "r:m:l:v:o:k:n:cdg")) != -1) {
+ while (1) {
+ int opt;
+ int option_index = 0;
+
+ opt = getopt_long(argc, argv, "h", opts, &option_index);
+ if (opt == -1)
+ break;
+
switch (opt) {
- case 'r':
+ case OPT_INCLUDE:
+ if (num_includes >= ARRAY_SIZE(includes)) {
+ fprintf(stderr, "error: too many includes\n");
+ exit(EXIT_INVALID_USAGE);
+ }
+ includes[num_includes++] = optarg;
+ break;
+ case OPT_INCLUDE_DEFAULTS:
+ if (num_includes >= ARRAY_SIZE(includes)) {
+ fprintf(stderr, "error: too many includes\n");
+ exit(EXIT_INVALID_USAGE);
+ }
+ includes[num_includes++] = DEFAULT_INCLUDE_PATH_PLACEHOLDER;
+ break;
+ case OPT_RULES:
rules = optarg;
break;
- case 'm':
+ case OPT_MODEL:
model = optarg;
break;
- case 'l':
+ case OPT_LAYOUT:
layout = optarg;
break;
- case 'v':
+ case OPT_VARIANT:
variant = optarg;
break;
- case 'o':
+ case OPT_OPTION:
options = optarg;
break;
- case 'k':
+ case OPT_KEYMAP:
keymap_path = optarg;
break;
- case 'n':
- errno = 0;
- evdev_offset = strtol(optarg, NULL, 10);
- if (errno) {
- fprintf(stderr, "error: -n option expects a number\n");
- exit(EXIT_INVALID_USAGE);
- }
+ case OPT_WITHOUT_X11_OFFSET:
+ evdev_offset = 0;
break;
- case 'c':
+ case OPT_REPORT_STATE:
report_state_changes = true;
break;
- case 'd':
+ case OPT_COMPOSE:
with_compose = true;
break;
- case 'g':
- consumed_mode = XKB_CONSUMED_MODE_GTK;
+ case OPT_CONSUMED_MODE:
+ if (strcmp(optarg, "gtk") == 0) {
+ consumed_mode = XKB_CONSUMED_MODE_GTK;
+ } else if (strcmp(optarg, "xkb") == 0) {
+ consumed_mode = XKB_CONSUMED_MODE_XKB;
+ } else {
+ fprintf(stderr, "error: invalid --consumed-mode \"%s\"\n", optarg);
+ usage(stderr, argv[0]);
+ return EXIT_INVALID_USAGE;
+ }
break;
+ case 'h':
+ usage(stdout, argv[0]);
+ return EXIT_SUCCESS;
case '?':
- fprintf(stderr, " Usage: %s [-r <rules>] [-m <model>] "
- "[-l <layout>] [-v <variant>] [-o <options>]\n",
- argv[0]);
- fprintf(stderr, " or: %s -k <path to keymap file>\n",
- argv[0]);
- fprintf(stderr, "For both: -n <evdev keycode offset>\n"
- " -c (to report changes to the state)\n"
- " -d (to enable compose)\n"
- " -g (to use GTK consumed mode)\n");
- exit(EXIT_INVALID_USAGE);
+ usage(stderr, argv[0]);
+ return EXIT_INVALID_USAGE;
}
}
- ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ ctx = xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES);
if (!ctx) {
fprintf(stderr, "Couldn't create xkb context\n");
goto out;
}
+ if (num_includes == 0)
+ includes[num_includes++] = DEFAULT_INCLUDE_PATH_PLACEHOLDER;
+
+ for (size_t i = 0; i < num_includes; i++) {
+ const char *include = includes[i];
+ if (strcmp(include, DEFAULT_INCLUDE_PATH_PLACEHOLDER) == 0)
+ xkb_context_include_path_append_default(ctx);
+ else
+ xkb_context_include_path_append(ctx, include);
+ }
+
if (keymap_path) {
FILE *file = fopen(keymap_path, "rb");
if (!file) {