diff options
Diffstat (limited to 'tools/compile-keymap.c')
-rw-r--r-- | tools/compile-keymap.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/tools/compile-keymap.c b/tools/compile-keymap.c new file mode 100644 index 0000000..e090de1 --- /dev/null +++ b/tools/compile-keymap.c @@ -0,0 +1,342 @@ +/* + * Copyright © 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "xkbcomp/xkbcomp-priv.h" +#include "xkbcomp/rules.h" +#include "xkbcommon/xkbcommon.h" + +#define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__" + +static bool verbose = false; +static enum output_format { + FORMAT_RMLVO, + FORMAT_KEYMAP, + FORMAT_KCCGST, + FORMAT_KEYMAP_FROM_XKB, +} output_format = FORMAT_KEYMAP; +static darray(const char *) includes; + +static void +usage(char **argv) +{ + printf("Usage: %s [OPTIONS]\n" + "\n" + "Compile the given RMLVO to a keymap and print it\n" + "\n" + "Options:\n" + " --verbose\n" + " Enable verbose debugging output\n" + " --kccgst\n" + " Print a keymap which only includes the KcCGST component names instead of the full keymap\n" + " --rmlvo\n" + " Print the full RMLVO with the defaults filled in for missing elements\n" + " --from-xkb\n" + " Load the XKB file from stdin, ignore RMLVO options. This option\n" + " must not be used with --kccgst.\n" + " --include\n" + " Add the given path to the include path list. This option is\n" + " order-dependent, include paths given first are searched first.\n" + " If an include path is given, the default include path list is\n" + " not used. Use --include-defaults to add the default include\n" + " paths\n" + " --include-defaults\n" + " Add the default set of include directories.\n" + " This option is order-dependent, include paths given first\n" + " are searched first.\n" + "\n" + "XKB-specific options:\n" + " --rules <rules>\n" + " The XKB ruleset (default: '%s')\n" + " --model <model>\n" + " The XKB model (default: '%s')\n" + " --layout <layout>\n" + " The XKB layout (default: '%s')\n" + " --variant <variant>\n" + " The XKB layout variant (default: '%s')\n" + " --options <options>\n" + " The XKB options (default: '%s')\n" + "\n", + argv[0], DEFAULT_XKB_RULES, + DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT, + DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "<none>", + DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : "<none>"); +} + +static bool +parse_options(int argc, char **argv, struct xkb_rule_names *names) +{ + enum options { + OPT_VERBOSE, + OPT_KCCGST, + OPT_RMLVO, + OPT_FROM_XKB, + OPT_INCLUDE, + OPT_INCLUDE_DEFAULTS, + OPT_RULES, + OPT_MODEL, + OPT_LAYOUT, + OPT_VARIANT, + OPT_OPTION, + }; + static struct option opts[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, OPT_VERBOSE}, + {"kccgst", no_argument, 0, OPT_KCCGST}, + {"rmlvo", no_argument, 0, OPT_RMLVO}, + {"from-xkb", no_argument, 0, OPT_FROM_XKB}, + {"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}, + {0, 0, 0, 0}, + }; + + while (1) { + int c; + int option_index = 0; + c = getopt_long(argc, argv, "h", opts, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(argv); + exit(0); + case OPT_VERBOSE: + verbose = true; + break; + case OPT_KCCGST: + output_format = FORMAT_KCCGST; + break; + case OPT_RMLVO: + output_format = FORMAT_RMLVO; + break; + case OPT_FROM_XKB: + output_format = FORMAT_KEYMAP_FROM_XKB; + break; + case OPT_INCLUDE: + darray_append(includes, optarg); + break; + case OPT_INCLUDE_DEFAULTS: + darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER); + break; + case OPT_RULES: + names->rules = optarg; + break; + case OPT_MODEL: + names->model = optarg; + break; + case OPT_LAYOUT: + names->layout = optarg; + break; + case OPT_VARIANT: + names->variant = optarg; + break; + case OPT_OPTION: + names->options = optarg; + break; + default: + usage(argv); + exit(1); + } + + } + + return true; +} + +static bool +print_rmlvo(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) +{ + printf("rules: \"%s\"\nmodel: \"%s\"\nlayout: \"%s\"\nvariant: \"%s\"\noptions: \"%s\"\n", + rmlvo->rules, rmlvo->model, rmlvo->layout, + rmlvo->variant ? rmlvo->variant : "", + rmlvo->options ? rmlvo->options : ""); + return true; +} + +static bool +print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) +{ + struct xkb_component_names kccgst; + + if (!xkb_components_from_rules(ctx, rmlvo, &kccgst)) + return false; + + printf("xkb_keymap {\n" + " xkb_keycodes { include \"%s\" };\n" + " xkb_types { include \"%s\" };\n" + " xkb_compat { include \"%s\" };\n" + " xkb_symbols { include \"%s\" };\n" + "};\n", + kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); + free(kccgst.keycodes); + free(kccgst.types); + free(kccgst.compat); + free(kccgst.symbols); + + return true; +} + +static bool +print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) +{ + struct xkb_keymap *keymap; + + keymap = xkb_keymap_new_from_names(ctx, rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (keymap == NULL) + return false; + + printf("%s\n", xkb_keymap_get_as_string(keymap, + XKB_KEYMAP_FORMAT_TEXT_V1)); + xkb_keymap_unref(keymap); + return true; +} + +static bool +print_keymap_from_file(struct xkb_context *ctx) +{ + struct xkb_keymap *keymap = NULL; + char *keymap_string = NULL; + FILE *file = NULL; + bool success = false; + + file = tmpfile(); + if (!file) { + fprintf(stderr, "Failed to create tmpfile\n"); + goto out; + } + + while (true) { + char buf[4096]; + size_t len; + + len = fread(buf, 1, sizeof(buf), stdin); + if (ferror(stdin)) { + fprintf(stderr, "Failed to read from stdin\n"); + goto out; + } + if (len > 0) { + size_t wlen = fwrite(buf, 1, len, file); + if (wlen != len) { + fprintf(stderr, "Failed to write to tmpfile\n"); + goto out; + } + } + if (feof(stdin)) + break; + } + fseek(file, 0, SEEK_SET); + keymap = xkb_keymap_new_from_file(ctx, file, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + if (!keymap) { + fprintf(stderr, "Couldn't create xkb keymap\n"); + goto out; + } + + keymap_string = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + if (!keymap_string) { + fprintf(stderr, "Couldn't get the keymap string\n"); + goto out; + } + + fputs(keymap_string, stdout); + success = true; + +out: + if (file) + fclose(file); + xkb_keymap_unref(keymap); + free(keymap_string); + + return success; +} + +int +main(int argc, char **argv) +{ + struct xkb_context *ctx; + struct xkb_rule_names names = { + .rules = NULL, + .model = NULL, + .layout = NULL, + .variant = NULL, + .options = NULL, + }; + int rc = 1; + const char **path; + + if (argc <= 1) { + usage(argv); + return 1; + } + + if (!parse_options(argc, argv, &names)) + return 1; + + ctx = xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES); + assert(ctx); + + if (verbose) { + xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_DEBUG); + xkb_context_set_log_verbosity(ctx, 10); + } + + xkb_context_sanitize_rule_names(ctx, &names); + if (darray_empty(includes)) + darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER); + + darray_foreach(path, includes) { + if (streq(*path, DEFAULT_INCLUDE_PATH_PLACEHOLDER)) + xkb_context_include_path_append_default(ctx); + else + xkb_context_include_path_append(ctx, *path); + } + + if (output_format == FORMAT_RMLVO) { + rc = print_rmlvo(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; + } else if (output_format == FORMAT_KEYMAP) { + rc = print_keymap(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; + } else if (output_format == FORMAT_KCCGST) { + rc = print_kccgst(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; + } else if (output_format == FORMAT_KEYMAP_FROM_XKB) { + rc = print_keymap_from_file(ctx); + } + + xkb_context_unref(ctx); + + return rc; +} |