/*
 * WARNING: This file is automatically generated by: {{ script }}
 *          Do not edit manually!
 */

/*
 * Copyright © 2024-2025 Pierre Le Marre <dev@wismill.eu>
 * SPDX-License-Identifier: MIT
 */

#include "config.h"

#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include "test.h"
#include "utils.h"
{% if not compiler.is_default %}
#include "external_xkb_compilers.h"
{% endif %}


enum update_files {
    NO_UPDATE = 0,
    UPDATE_USING_TEST_INPUT,
    UPDATE_USING_TEST_OUTPUT
};

#define GOLDEN_TESTS_OUTPUTS "keymaps/merge-modes/"

{% if compiler.is_default %}

/* xkbcommon buffer compiler */
static struct xkb_keymap *
compile_buffer(struct xkb_context *context, enum xkb_keymap_format format,
               const char *buf, size_t len, void *private)
{
    return test_compile_buffer(context, format, buf, len);
}

static bool
test_compile(struct xkb_context *ctx, const char* xkb_root,
             const char *test_title,
             const char *keymap_str, size_t keymap_len,
             const char *path, bool update)
{
    return test_compile_output(ctx, XKB_KEYMAP_FORMAT_TEXT_V2,
                               XKB_KEYMAP_USE_ORIGINAL_FORMAT, compile_buffer,
                               NULL, test_title, keymap_str, keymap_len,
                               path, !!update);
}

{% else %}

/* Third-party compiler: {{ compiler.name }} */
struct compile_buffer_{{ compiler.name }}_data {
    const char* xkb_root;
};

static int
compile_buffer_{{ compiler.name }}(const char *buf, size_t len, void *private,
                                   char* *out, size_t *out_size)
{
    struct compile_buffer_{{ compiler.name }}_data *data = private;
    {% if compiler.name == "xkbcomp" %}
    /* Xorg xkbcomp */
    return compile_with_xkbcomp(NULL, data->xkb_root, buf, len, out, out_size);
    {% elif compiler.name == "kbvm" %}
    /* kbvm */
    return compile_with_kbvm(data->xkb_root, buf, len, out, out_size);
    {% else %}
    #error "Unsupported compiler: {{ compiler.name }}"
    {% endif %}
}

static bool
test_compile(struct xkb_context *ctx, const char* xkb_root,
             const char *test_title,
             const char *keymap_str, size_t keymap_len,
             const char *path, bool update)
{
    struct compile_buffer_{{ compiler.name }}_data data = { .xkb_root = xkb_root };
    return test_third_pary_compile_output(compile_buffer_{{ compiler.name}},
                                          &data, test_title,
                                          keymap_str, keymap_len,
                                          path, !!update);
}
{% endif %}

struct keymap_test_data {
    const char* title;
    const char* file;
    const char* keymap;
    const char* expected;
};

{% if compiler.extended_syntax %}
static void
test_vmods(struct xkb_context *ctx, const char* xkb_root,
           enum update_files update) {

#define BASE_VMODS                                                             \
    "virtual_modifiers U1, U2, U3, U4, U5, U6;\n"                              \
    "virtual_modifiers Z1=none, Z2=none, Z3=none, Z4=none, Z5=none, Z6=none;\n"\
    "virtual_modifiers M1=0x1000, M2=0x2000, M3=0x3000, "                      \
                      "M4=0x4000, M5=0x5000, M6=0x6000;\n"

    const struct keymap_test_data tests[] = {
        /*
         * Change virtual modifiers in same component
         */

        {
            .title = "same-component-include-default-augment",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                "   include \"merge_modes_vmods(base)|merge_modes_vmods(new)\"\n"
                "  };\n"
                "  xkb_compat \"\" { interpret.repeat= True; };\n"
                "};",
            .file = "same-component-include-default-augment",
        },
        {
            .title = "same-component-include-default-override",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                "   include \"merge_modes_vmods(base)+merge_modes_vmods(new)\"\n"
                "  };\n"
                "  xkb_compat \"\" { interpret.repeat= True; };\n"
                "};",
            .file = "same-component-include-default-override",
        },

        /*
         * Change virtual modifiers in another component
         */

        {
            .title = "other-component-direct",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types {\n"
                BASE_VMODS
                "  };\n"
                "  xkb_compat \"\" {\n"
                "    augment  virtual_modifiers U1, U2=none, U3=0x300000;\n"
                "    override virtual_modifiers U4, U5=none, U6=0x600000;\n"
                "    augment  virtual_modifiers Z1, Z2=none, Z3=0x310000;\n"
                "    override virtual_modifiers Z4, Z5=none, Z6=0x610000;\n"
                "    augment  virtual_modifiers M1, M2=none, M3=0x320000;\n"
                "    override virtual_modifiers M4, M5=none, M6=0x620000;\n"
                "  };\n"
                "};",
            .file = "other-component-direct",
        },
        {
            .title = "other-component-include-default",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                BASE_VMODS
                "  };\n"
                "  xkb_compat \"\" { include \"merge_modes_vmods(new)\" };\n"
                "};",
            .file = "same-component-include-default-override",
        },
        {
            .title = "other-component-include-augment",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                BASE_VMODS
                "  };\n"
                "  xkb_compat \"\" { augment \"merge_modes_vmods(new)\" };\n"
                "};",
            .file = "same-component-include-default-augment",
        },
        {
            .title = "other-component-include-override",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                BASE_VMODS
                "  };\n"
                "  xkb_compat \"\" { override \"merge_modes_vmods(new)\" };\n"
                "};",
            .file = "same-component-include-default-override",
        },
        {
            .title = "other-component-include-replace",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_types \"\" {\n"
                BASE_VMODS
                "  };\n"
                "  xkb_compat \"\" { replace \"merge_modes_vmods(new)\" };\n"
                "};",
            .file = "same-component-include-default-override",
        },
    };

    for (unsigned int k = 0; k < ARRAY_SIZE(tests); k++) {
        const struct keymap_test_data* const test = &tests[k];

        char title[1024] = { 0 };
        snprintf(title, sizeof(title), "%s #%u: %s", __func__, k, test->title);

        char path[PATH_MAX] = { 0 };
        snprintf(path, sizeof(path),
                 GOLDEN_TESTS_OUTPUTS "vmods-%s{{ compiler.suffix }}.xkb",
                 test->file);

        assert(test_compile(ctx, xkb_root, title,
                            test->keymap, strlen(test->keymap),
                            path, !!update));
    }
}
{% endif %}

static void
test_compat(struct xkb_context *ctx, const char* xkb_root,
            enum update_files update)
{
    // Github Issue #566
    const char keymap_str[] =
        "xkb_keymap {\n"
        "  xkb_keycodes { <> = 8; };\n"
        "  xkb_compat {\n"
        "    interpret A { repeat = true; };\n"
        "    interpret A { repeat = true; };\n"
        "    interpret A { action = SetMods(mods=Mod1); };\n"
        "    interpret B { repeat = true; };\n"
        "    interpret B { repeat = true; };\n"
        "    augment interpret B { action = SetMods(mods=Mod1); };\n"
        "    interpret C { repeat = true; };\n"
        "    interpret C { repeat = true; };\n"
        "    override interpret C { action = SetMods(mods=Mod1); };\n"
        "    interpret D { repeat = true; };\n"
        "    interpret D { repeat = true; };\n"
        "    replace interpret D { action = SetMods(mods=Mod1); };\n"
        "\n"
	    "    indicator \"A\" { modifiers=Shift; };\n"
	    "    indicator \"A\" { modifiers=Lock; };\n"
	    "    indicator \"A\" { groups= Group1; };\n"
	    "    indicator \"B\" { modifiers=Shift; };\n"
	    "    indicator \"B\" { modifiers=Lock; };\n"
	    "    augment indicator \"B\" { groups=Group1; };\n"
	    "    indicator \"C\" { modifiers=Shift; };\n"
	    "    indicator \"C\" { modifiers=Lock; };\n"
	    "    override indicator \"C\" { groups=Group1; };\n"
	    "    indicator \"D\" { modifiers=Shift; };\n"
	    "    indicator \"D\" { modifiers=Lock; };\n"
	    "    replace indicator \"D\" { groups=Group1; };\n"
        "  };\n"
        "  xkb_symbols { key <> { }; };\n"
        "};\n";

    assert(test_compile(ctx, xkb_root, "test_merge_mode: compat",
                        keymap_str, ARRAY_SIZE(keymap_str) - 1,
                        GOLDEN_TESTS_OUTPUTS "compat{{ compiler.suffix }}.xkb",
                        !!update));
}

static void
test_merge_modes(struct xkb_context *ctx, const char* xkb_root,
                 enum update_files update) {
    const struct keymap_test_data tests[] = {
    {% for group in tests %}
        /*
         * #{{ loop.index0 }}{{ " " + group.title if group.title else "" }}
         */
        {% for test in group.tests %}
        {
            .title = "#{{ loop.index0 }} {{ test.title | escape_quotes }}",
            .file = "{{ test.file | string | escape_quotes }}",
            .keymap =
                {%- for line in test.content.splitlines() +%}
                "{{ line | escape_quotes }}\n"
                {%- endfor %},
            .expected =
                {% for line in test.expected.splitlines() %}
                "{{ line | escape_quotes }}\n"
                {% endfor %}
        },
        {% endfor %}
    {% endfor %}
    };

    for (unsigned int k = 0; k < ARRAY_SIZE(tests); k++) {
        const struct keymap_test_data* const test = &tests[k];

        char title[1024] = { 0 };
        snprintf(title, sizeof(title), "%s #%u: %s", __func__, k, test->title);

        char path[PATH_MAX] = { 0 };
    {% if compiler.is_default %}
        snprintf(path, sizeof(path), GOLDEN_TESTS_OUTPUTS "%s.xkb", test->file);
    {% else %}
        snprintf(path, sizeof(path),
                 GOLDEN_TESTS_OUTPUTS "%02u-%s{{ compiler.suffix }}.xkb",
                 k, test->file);
    {% endif %}

        const char* const keymap = (update == UPDATE_USING_TEST_INPUT)
            ? test->expected
            : test->keymap;
        assert(test_compile(ctx, xkb_root, title, keymap, strlen(keymap),
                            path, !!update));
    }
}

static void
test_defaults(struct xkb_context *ctx, const char* xkb_root,
              enum update_files update)
{
    const struct keymap_test_data tests[] = {
        {
            .title = "plain",
            .file = "plain",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_keycodes {\n"
                "    <A> = 38;\n"
                "    <S> = 39;\n"
                "    <D> = 40;\n"
                "    <F> = 41;\n"
                "    <G> = 42;\n"
                "\n"
                "    indicator 1 = \"A\";\n"
                "    indicator 2 = \"B\";\n"
                "    indicator 3 = \"C\";\n"
                "  };\n"
                "  xkb_types {\n"
                "    type \"ONE_LEVEL\" {\n"
                "        map[None] = 1;\n"
                "    };\n"
                "    type \"ALPHABETIC\" {\n"
                "        modifiers = Shift + Lock;\n"
                "        map[Shift] = 2;\n"
                "        map[Lock]  = 2;\n"
                "    };\n"
                "  };\n"
                "  xkb_compat {\n"
                "    virtual_modifiers M1=0x1000, M2=0x2000, M3=0x4000, M4=0x8000;\n"
                "\n"
                "    interpret.repeat= False;\n"
                "    // Effective: override\n"
                "    interpret.repeat= True;\n"
                "\n"
                "    interpret.virtualmod = M1;\n"
                "    // Effective: override\n"
                "    interpret.virtualmod = M2;\n"
                "    // Ineffective: cannot augment previous value\n"
                "    augment interpret.virtualmod = M3;\n"
                "\n"
                "    SetGroup.group = 1;\n"
                "    // Effective: override\n"
                "    SetGroup.group = 2;\n"
                "    // Ineffective: cannot augment previous value\n"
                "    augment SetGroup.group = 3;\n"
                "\n"
                "\n"
                "    // Effective: *override* every explicit field\n"
                "    interpret a {\n"
                "        virtualmod = M4;\n"
                "        action=SetGroup(group=1);\n"
                "    };\n"
                "    interpret A {\n"
                "        repeat = False;\n"
                "        virtualmod = M1;\n"
                "        action=SetMods(mods=M1);\n"
                "    };\n"
                "\n"
                "\n"
                "    // Effective: no previous interpret entry,\n"
                "    // so *override* every explicit field\n"
                "    augment interpret s {\n"
                "        repeat = False;\n"
                "        action=SetGroup();\n"
                "    };\n"
                "    // Only augment fields not set previously (explicit or implicit)\n"
                "    augment interpret s {\n"
                "        // Ineffective: cannot augment previous implicit value\n"
                "        virtualmod = M4;\n"
                "        // Ineffective: cannot augment previous explicit value\n"
                "        action=SetGroup(group=4);\n"
                "        // Effective: no previous value\n"
                "        useModMapMods=level1;\n"
                "    };\n"
                "    augment interpret S {\n"
                "       action=SetGroup(group=4);\n"
                "    };\n"
                "    interpret g {\n"
                "      virtualmod = M3;\n"
                "      repeat = False;"
                "      action = SetGroup(group=3);\n"
                "    };\n"
                "    indicator \"C\" {\n"
                "      modifiers = M3;\n"
                "      whichmodstate = locked;\n"
                "    };\n"
                "\n"
                "\n"
                "    indicator.modifiers = M1;\n"
                "    indicator.modifiers = M2;\n"
                "    augment indicator.modifiers = M3;\n"
                "    indicator.groups = All - 1;\n"
                "    indicator.controls = AudibleBell;\n"
                "\n"
                "    indicator \"A\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = base;\n"
                "    };\n"
                "    indicator \"A\" {\n"
                "      whichmodstate = locked;\n"
                "      whichgroupstate = locked;\n"
                "      controls = SlowKeys;\n"
                "    };\n"
                "    augment indicator \"B\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = base;\n"
                "    };\n"
                "    augment indicator \"B\" {\n"
                "      groups = 4;\n"
                "      whichmodstate   = locked;\n"
                "      whichgroupstate = locked;\n"
                "    };\n"
                "  };\n"
                "  xkb_symbols {\n"
                "      name[1] = \"xxx\";\n"
                "      name[1] = \"yyy\";\n"
                "      augment name[1] = \"zzz\";\n"
                "\n"
                "      key <A> { [a, A] };\n"
                "      key <S> { [s, S] };\n"
                "      modifier_map Shift   { <A> };\n"
                "      modifier_map Control { <S> };\n"
                "\n"
                "      key <G> { type = \"ONE_LEVEL\", repeat=true, [g] };"
                "      modifier_map Mod3 { <G> };\n"
                "\n"
                "      key.repeat = false;\n"
                "      key.vmods = M1;\n"
                "      key.vmods = M2;\n"
                "      augment key.vmods = M3;\n"
                "\n"
                "      key <D>         { vmods = M4, [d, D] };\n"
                "      augment key <F> { vmods = M4, [f, F] };\n"
                "      augment key <F> { vmods = M1, repeat = true };\n"
                "  };\n"
                "};\n",
            .expected =
                "xkb_keymap {\n"
                "  xkb_keycodes {\n"
                "    <A> = 38;\n"
                "    <S> = 39;\n"
                "    <D> = 40;\n"
                "    <F> = 41;\n"
                "    indicator 1 = \"A\";\n"
                "    indicator 2 = \"B\";\n"
                "    indicator 3 = \"C\";\n"
                "  };\n"
                "  xkb_types {\n"
                "    type \"ONE_LEVEL\" {\n"
                "        map[None] = 1;\n"
                "    };\n"
                "    type \"ALPHABETIC\" {\n"
                "        modifiers = Shift + Lock;\n"
                "        map[Shift] = 2;\n"
                "        map[Lock]  = 2;\n"
                "    };\n"
                "  };\n"
                "  xkb_compat {\n"
                "    virtual_modifiers M1=0x1000, M2=0x2000, M3=0x4000, M4=0x8000;\n"
                "\n"
                "    interpret.repeat= True;\n"
                "    interpret.virtualmod = M2;\n"
                "    SetGroup.group = 2;\n"
                "\n"
                "    interpret a {\n"
                "        virtualmod = M4;\n"
                "        action=SetGroup(group=1);\n"
                "    };\n"
                "    interpret A {\n"
                "        repeat = False;\n"
                "        virtualmod = M1;\n"
                "        action=SetMods(mods=M1);\n"
                "    };\n"
                "    interpret s {\n"
                "        repeat = False;\n"
                "        action=SetGroup(group=2);\n"
                "        useModMapMods=level1;\n"
                "    };\n"
                "    interpret S {\n"
                "       action=SetGroup(group=4);"
                "    };\n"
                "\n"
                "    indicator.modifiers = M2;\n"
                "    indicator.groups = All - 1;\n"
                "    indicator.controls = AudibleBell;\n"
                "\n"
                "    indicator \"A\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = locked;\n"
                "      whichgroupstate = locked;\n"
                "      controls = SlowKeys;\n"
                "    };\n"
                "    indicator \"B\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = base;\n"
                "      whichgroupstate = locked;\n"
                "    };\n"
                "  };\n"
                "  xkb_symbols {\n"
                "      name[1] = \"yyy\";\n"
                "\n"
                "      key <A> { [a, A] };\n"
                "      key <S> { [s, S] };\n"
                "      modifier_map Shift   { <A> };\n"
                "      modifier_map Control { <S> };\n"
                "\n"
                "      key <D> { vmods = M4, repeat = false, [d, D] };\n"
                "      key <F> { vmods = M4, repeat = true , [f, F] };\n"
                "      key <G> { type = \"ONE_LEVEL\", repeat=true, vmods=M3, [g] };"
                "  };\n"
                "};\n"
        },
        {
            .title = "include",
            .file = "include",
            .keymap =
                "xkb_keymap {\n"
                "  xkb_keycodes {\n"
                "    <A> = 38;\n"
                "    <S> = 39;\n"
                "    <D> = 40;\n"
                "    <F> = 41;\n"
                "    <G> = 42;\n"
                "\n"
                "    indicator 1 = \"A\";\n"
                "    indicator 2 = \"B\";\n"
                "    indicator 3 = \"C\";\n"
                "  };\n"
                "  xkb_types {\n"
                "    type \"ONE_LEVEL\" {\n"
                "        map[None] = 1;\n"
                "    };\n"
                "    type \"ALPHABETIC\" {\n"
                "        modifiers = Shift + Lock;\n"
                "        map[Shift] = 2;\n"
                "        map[Lock]  = 2;\n"
                "    };\n"
                "  };\n"
                "  xkb_compat {\n"
                "    virtual_modifiers M1,M2,M3;"
                "    interpret.virtualmod = M3;\n"
                "    indicator.modifiers = M3;\n"
                "    SetGroup.group = 3;\n"
                "\n"
                "    include \"merge_modes_defaults\"\n"
                "\n"
                "    interpret g {\n"
                "      action = SetGroup();\n"
                "    };\n"
                "    indicator \"C\" {\n"
                "      whichmodstate = locked;\n"
                "    };\n"
                "  };\n"
                "  xkb_symbols {\n"
                "      name[1] = \"XXX\";\n"
                "\n"
                "      key.type = \"ONE_LEVEL\";"
                "\n"
                "      include \"merge_modes_defaults\"\n"
                "\n"
                "      key <G> { repeat = true, [g, G] };\n"
                "      modifier_map Mod3 { <G> };\n"
                "  };\n"
                "};\n",
            .expected =
                "xkb_keymap {\n"
                "  xkb_keycodes {\n"
                "    <A> = 38;\n"
                "    <S> = 39;\n"
                "    <D> = 40;\n"
                "    <F> = 41;\n"
                "    indicator 1 = \"A\";\n"
                "    indicator 2 = \"B\";\n"
                "    indicator 3 = \"C\";\n"
                "  };\n"
                "  xkb_types {\n"
                "    type \"ONE_LEVEL\" {\n"
                "        map[None] = 1;\n"
                "    };\n"
                "    type \"ALPHABETIC\" {\n"
                "        modifiers = Shift + Lock;\n"
                "        map[Shift] = 2;\n"
                "        map[Lock]  = 2;\n"
                "    };\n"
                "  };\n"
                "  xkb_compat {\n"
                "    virtual_modifiers M1=0x1000, M2=0x2000, M3=0x4000, M4=0x8000;\n"
                "\n"
                "    interpret.repeat= True;\n"
                "    interpret.virtualmod = M2;\n"
                "    SetGroup.group = 2;\n"
                "\n"
                "    interpret a {\n"
                "        virtualmod = M4;\n"
                "        action=SetGroup(group=1);\n"
                "    };\n"
                "    interpret A {\n"
                "        repeat = False;\n"
                "        virtualmod = M1;\n"
                "        action=SetMods(mods=M1);\n"
                "    };\n"
                "    interpret s {\n"
                "        repeat = False;\n"
                "        action=SetGroup(group=2);\n"
                "        useModMapMods=level1;\n"
                "    };\n"
                "    interpret S {\n"
                "       action=SetGroup(group=4);\n"
                "    };\n"
                "\n"
                "    indicator.modifiers = M2;\n"
                "    indicator.groups = All - 1;\n"
                "    indicator.controls = AudibleBell;\n"
                "\n"
                "    indicator \"A\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = locked;\n"
                "      whichgroupstate = locked;\n"
                "      controls = SlowKeys;\n"
                "    };\n"
                "    indicator \"B\" {\n"
                "      modifiers = M4;\n"
                "      whichmodstate = base;\n"
                "      whichgroupstate = locked;\n"
                "    };\n"
                "  };\n"
                "  xkb_symbols {\n"
                "      name[1] = \"yyy\";\n"
                "\n"
                "      key <A> { [a, A] };\n"
                "      key <S> { [s, S] };\n"
                "      modifier_map Shift   { <A> };\n"
                "      modifier_map Control { <S> };\n"
                "\n"
                "      key <D> { vmods = M4, repeat = false, [d, D] };\n"
                "      key <F> { vmods = M4, repeat = true , [f, F] };\n"
                "      key <G> { vmods = M3, [g] };\n"
                "  };\n"
                "};\n"
        }
    };

    for (unsigned int k = 0; k < ARRAY_SIZE(tests); k++) {
        const struct keymap_test_data* const test = &tests[k];

        char title[1024] = { 0 };
        snprintf(title, sizeof(title), "%s #%u: %s", __func__, k, test->title);

        char path[PATH_MAX] = { 0 };
        snprintf(path, sizeof(path), GOLDEN_TESTS_OUTPUTS "defaults-%s{{ compiler.suffix }}.xkb",
                 test->file);

        assert(test_compile(ctx, xkb_root, title,
                            test->keymap, strlen(test->keymap),
                            path, !!update));
    }
}

int
main(int argc, char *argv[])
{
    test_init();

    /* Check if we run the tests or just update their outputs */
    enum update_files update_output_files = NO_UPDATE;
    if (argc > 1) {
        if (streq(argv[1], "update")) {
            /* Update files with *expected* results */
            update_output_files = UPDATE_USING_TEST_INPUT;
        } else if (streq(argv[1], "update-obtained")) {
            /* Update files with *obtained* results */
            update_output_files = UPDATE_USING_TEST_OUTPUT;
        } else {
            fprintf(stderr, "ERROR: unsupported argument: \"%s\".\n", argv[1]);
            exit(EXIT_FAILURE);
        }
    }

    struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG);
    assert(ctx);

    char* xkb_root = test_get_path("");
    assert(xkb_root);

    {% if compiler.extended_syntax %}
    test_vmods(ctx, xkb_root, update_output_files);
    {% endif %}
    test_compat(ctx, xkb_root, update_output_files);
    test_merge_modes(ctx, xkb_root, update_output_files);
    test_defaults(ctx, xkb_root, update_output_files);

    free(xkb_root);
    xkb_context_unref(ctx);

    return EXIT_SUCCESS;
}
