Mark ab/7061308 as merged in stage.

Bug: 180401296
Merged-In: Ie43615061948e35099344d3da2dd1b92bf0eac8c
Change-Id: I815c62afd2ec874c143cfa55d64b547530e54517
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index eb4e74f..c864097 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -76,7 +76,7 @@
           Invoke-WebRequest -Uri https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip -OutFile win_flex_bison.zip
           Expand-Archive -Path win_flex_bison.zip -DestinationPath bin
           Expand-Archive -Path ninja.zip -DestinationPath bin
-          Write-Output ("::add-path::" + (Get-Location) + "./bin")
+          Write-Output ((Get-Location).ToString() + "./bin") | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
       - name: Setup
         shell: cmd
         run: |
diff --git a/Android.bp b/Android.bp
index 9524262..c766cd5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,8 +12,43 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["external_libxkbcommon_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "external_libxkbcommon_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-GPL",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-GPL-3.0",
+        "SPDX-license-identifier-ISC",
+        "SPDX-license-identifier-MIT",
+        "legacy_unencumbered",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
+
 cc_library_static {
     srcs: [
+        "config/libxkbcommon.so.0.0.0.p/parser.c",
         "src/compose/parser.c",
         "src/compose/paths.c",
         "src/compose/state.c",
@@ -27,7 +62,6 @@
         "src/xkbcomp/keymap.c",
         "src/xkbcomp/keymap-dump.c",
         "src/xkbcomp/keywords.c",
-        "src/xkbcomp/parser.c",
         "src/xkbcomp/rules.c",
         "src/xkbcomp/scanner.c",
         "src/xkbcomp/symbols.c",
@@ -55,7 +89,7 @@
         // Needed because libxkbcommon uses GNU extension asprintf().
         "-D_GNU_SOURCE",
     ],
-    local_include_dirs: ["src"],
+    local_include_dirs: ["src", "config/libxkbcommon.so.0.0.0.p"],
     export_include_dirs: ["."],
     vendor_available: true,
     host_supported: true,
diff --git a/METADATA b/METADATA
index ffa117d..88a7c15 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,14 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/xkbcommon/libxkbcommon/archive/xkbcommon-1.0.1.tar.gz"
+    value: "https://github.com/xkbcommon/libxkbcommon/archive/xkbcommon-1.0.3.tar.gz"
   }
-  version: "xkbcommon-1.0.1"
-  license_type: NOTICE
+  version: "xkbcommon-1.0.3"
+  # would be NOTICE save for test/evdev-scancodes.h
+  license_type: RESTRICTED
   last_upgrade_date {
     year: 2020
-    month: 10
-    day: 28
+    month: 11
+    day: 24
   }
 }
diff --git a/NEWS b/NEWS
index fee6a59..968f183 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,22 @@
+libxkbcommon 1.0.3 - 2020-11-23
+==================
+
+- Fix (hopefully) a segfault in xkb_x11_keymap_new_from_device() in some
+  unclear situation (bug introduced in 1.0.2).
+
+- Fix keymaps created with xkb_x11_keymap_new_from_device() don't have level
+  names (bug introduced in 0.8.0).
+
+libxkbcommon 1.0.2 - 2020-11-20
+==================
+
+- Fix a bug where a keysym that cannot be resolved in a keymap gets compiled to
+  a garbage keysym. Now it is set to XKB_KEY_NoSymbol instead.
+
+- Improve the speed of xkb_x11_keymap_new_from_device() on repeated calls in the
+  same xkb_context().
+
+
 libxkbcommon 1.0.1 - 2020-09-11
 ==================
 
diff --git a/bench/x11.c b/bench/x11.c
new file mode 100644
index 0000000..2849385
--- /dev/null
+++ b/bench/x11.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2020 Ran Benita <ran@unusedvar.com>
+ *
+ * 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 <stdlib.h>
+
+#include <xcb/xkb.h>
+
+#include "xkbcommon/xkbcommon.h"
+#include "xkbcommon/xkbcommon-x11.h"
+
+#include "bench.h"
+
+#define BENCHMARK_ITERATIONS 2500
+
+int
+main(void)
+{
+    int ret;
+    xcb_connection_t *conn;
+    int32_t device_id;
+    struct xkb_context *ctx;
+    struct bench bench;
+    char *elapsed;
+
+    conn = xcb_connect(NULL, NULL);
+    if (!conn || xcb_connection_has_error(conn)) {
+        fprintf(stderr, "Couldn't connect to X server: error code %d\n",
+                conn ? xcb_connection_has_error(conn) : -1);
+        ret = -1;
+        goto err_out;
+    }
+
+    ret = xkb_x11_setup_xkb_extension(conn,
+                                      XKB_X11_MIN_MAJOR_XKB_VERSION,
+                                      XKB_X11_MIN_MINOR_XKB_VERSION,
+                                      XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
+                                      NULL, NULL, NULL, NULL);
+    if (!ret) {
+        fprintf(stderr, "Couldn't setup XKB extension\n");
+        goto err_conn;
+    }
+
+    device_id = xkb_x11_get_core_keyboard_device_id(conn);
+    if (device_id == -1) {
+        ret = -1;
+        fprintf(stderr, "Couldn't find core keyboard device\n");
+        goto err_conn;
+    }
+
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    if (!ctx) {
+        ret = -1;
+        fprintf(stderr, "Couldn't create xkb context\n");
+        goto err_conn;
+    }
+
+    bench_start(&bench);
+    for (int i = 0; i < BENCHMARK_ITERATIONS; i++) {
+        struct xkb_keymap *keymap;
+        struct xkb_state *state;
+
+        keymap = xkb_x11_keymap_new_from_device(ctx, conn, device_id,
+                                                XKB_KEYMAP_COMPILE_NO_FLAGS);
+        assert(keymap);
+
+        state = xkb_x11_state_new_from_device(keymap, conn, device_id);
+        assert(state);
+
+        xkb_state_unref(state);
+        xkb_keymap_unref(keymap);
+    }
+    bench_stop(&bench);
+    ret = 0;
+
+    elapsed = bench_elapsed_str(&bench);
+    fprintf(stderr, "retrieved %d keymaps from X in %ss\n",
+            BENCHMARK_ITERATIONS, elapsed);
+    free(elapsed);
+
+    xkb_context_unref(ctx);
+err_conn:
+    xcb_disconnect(conn);
+err_out:
+    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/config.h b/config.h
new file mode 120000
index 0000000..2d4dea9
--- /dev/null
+++ b/config.h
@@ -0,0 +1 @@
+config/config.h
\ No newline at end of file
diff --git a/config.h b/config/config.h
similarity index 96%
rename from config.h
rename to config/config.h
index 145bce1..76ab62a 100644
--- a/config.h
+++ b/config/config.h
@@ -45,7 +45,7 @@
 
 #define LIBXKBCOMMON_TOOL_PATH "/usr/local/libexec/xkbcommon"
 
-#define LIBXKBCOMMON_VERSION "1.0.1"
+#define LIBXKBCOMMON_VERSION "1.0.3"
 
 #define WIN32_LEAN_AND_MEAN 1
 
diff --git a/src/xkbcomp/parser.c b/config/libxkbcommon.so.0.0.0.p/parser.c
similarity index 98%
rename from src/xkbcomp/parser.c
rename to config/libxkbcommon.so.0.0.0.p/parser.c
index 5cc10e4..921673f 100644
--- a/src/xkbcomp/parser.c
+++ b/config/libxkbcommon.so.0.0.0.p/parser.c
@@ -675,8 +675,8 @@
      653,   655,   657,   661,   663,   665,   667,   669,   671,   673,
      675,   679,   681,   685,   689,   691,   693,   695,   699,   701,
      703,   705,   709,   710,   713,   715,   717,   719,   723,   727,
-     733,   734,   754,   755,   758,   759,   762,   765,   768,   771,
-     772,   775,   778,   779,   782
+     735,   736,   756,   757,   760,   761,   764,   767,   770,   773,
+     774,   777,   780,   781,   784
 };
 #endif
 
@@ -2890,21 +2890,23 @@
   case 169: /* KeySym: IDENT  */
 #line 728 "../src/xkbcomp/parser.y"
                         {
-                            if (!resolve_keysym((yyvsp[0].str), &(yyval.keysym)))
+                            if (!resolve_keysym((yyvsp[0].str), &(yyval.keysym))) {
                                 parser_warn(param, "unrecognized keysym \"%s\"", (yyvsp[0].str));
+                                (yyval.keysym) = XKB_KEY_NoSymbol;
+                            }
                             free((yyvsp[0].str));
                         }
-#line 2898 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2900 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 170: /* KeySym: SECTION  */
-#line 733 "../src/xkbcomp/parser.y"
+#line 735 "../src/xkbcomp/parser.y"
                                 { (yyval.keysym) = XKB_KEY_section; }
-#line 2904 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2906 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 171: /* KeySym: Integer  */
-#line 735 "../src/xkbcomp/parser.y"
+#line 737 "../src/xkbcomp/parser.y"
                         {
                             if ((yyvsp[0].num) < 0) {
                                 parser_warn(param, "unrecognized keysym \"%"PRId64"\"", (yyvsp[0].num));
@@ -2922,89 +2924,89 @@
                                 }
                             }
                         }
-#line 2926 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2928 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 172: /* SignedNumber: MINUS Number  */
-#line 754 "../src/xkbcomp/parser.y"
+#line 756 "../src/xkbcomp/parser.y"
                                         { (yyval.num) = -(yyvsp[0].num); }
-#line 2932 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2934 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 173: /* SignedNumber: Number  */
-#line 755 "../src/xkbcomp/parser.y"
+#line 757 "../src/xkbcomp/parser.y"
                                         { (yyval.num) = (yyvsp[0].num); }
-#line 2938 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2940 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 174: /* Number: FLOAT  */
-#line 758 "../src/xkbcomp/parser.y"
+#line 760 "../src/xkbcomp/parser.y"
                                 { (yyval.num) = (yyvsp[0].num); }
-#line 2944 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2946 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 175: /* Number: INTEGER  */
-#line 759 "../src/xkbcomp/parser.y"
+#line 761 "../src/xkbcomp/parser.y"
                                 { (yyval.num) = (yyvsp[0].num); }
-#line 2950 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2952 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 176: /* Float: FLOAT  */
-#line 762 "../src/xkbcomp/parser.y"
+#line 764 "../src/xkbcomp/parser.y"
                                 { (yyval.num) = 0; }
-#line 2956 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2958 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 177: /* Integer: INTEGER  */
-#line 765 "../src/xkbcomp/parser.y"
+#line 767 "../src/xkbcomp/parser.y"
                                 { (yyval.num) = (yyvsp[0].num); }
-#line 2962 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2964 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 178: /* KeyCode: INTEGER  */
-#line 768 "../src/xkbcomp/parser.y"
+#line 770 "../src/xkbcomp/parser.y"
                                 { (yyval.num) = (yyvsp[0].num); }
-#line 2968 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2970 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 179: /* Ident: IDENT  */
-#line 771 "../src/xkbcomp/parser.y"
+#line 773 "../src/xkbcomp/parser.y"
                                 { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); }
-#line 2974 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2976 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 180: /* Ident: DEFAULT  */
-#line 772 "../src/xkbcomp/parser.y"
+#line 774 "../src/xkbcomp/parser.y"
                                 { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "default"); }
-#line 2980 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2982 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 181: /* String: STRING  */
-#line 775 "../src/xkbcomp/parser.y"
+#line 777 "../src/xkbcomp/parser.y"
                                 { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); }
-#line 2986 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2988 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 182: /* OptMapName: MapName  */
-#line 778 "../src/xkbcomp/parser.y"
+#line 780 "../src/xkbcomp/parser.y"
                                 { (yyval.str) = (yyvsp[0].str); }
-#line 2992 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 2994 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 183: /* OptMapName: %empty  */
-#line 779 "../src/xkbcomp/parser.y"
+#line 781 "../src/xkbcomp/parser.y"
                                 { (yyval.str) = NULL; }
-#line 2998 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 3000 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
   case 184: /* MapName: STRING  */
-#line 782 "../src/xkbcomp/parser.y"
+#line 784 "../src/xkbcomp/parser.y"
                                 { (yyval.str) = (yyvsp[0].str); }
-#line 3004 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 3006 "libxkbcommon.so.0.0.0.p/parser.c"
     break;
 
 
-#line 3008 "libxkbcommon.so.0.0.0.p/parser.c"
+#line 3010 "libxkbcommon.so.0.0.0.p/parser.c"
 
       default: break;
     }
@@ -3198,7 +3200,7 @@
   return yyresult;
 }
 
-#line 785 "../src/xkbcomp/parser.y"
+#line 787 "../src/xkbcomp/parser.y"
 
 
 XkbFile *
diff --git a/src/xkbcomp/parser.h b/config/libxkbcommon.so.0.0.0.p/parser.h
similarity index 100%
rename from src/xkbcomp/parser.h
rename to config/libxkbcommon.so.0.0.0.p/parser.h
diff --git a/meson.build b/meson.build
index 6e433f4..47c436f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
 project(
     'libxkbcommon',
     'c',
-    version: '1.0.1',
+    version: '1.0.3',
     default_options: [
         'c_std=c99',
         'warning_level=2',
@@ -515,6 +515,8 @@
     'test/common.c',
     'test/test.h',
     'test/evdev-scancodes.h',
+    'bench/bench.c',
+    'bench/bench.h',
     libxkbcommon_sources,
     include_directories: include_directories('src'),
 )
@@ -677,38 +679,35 @@
 
 
 # Benchmarks.
-libxkbcommon_bench_internal = static_library(
-    'xkbcommon-bench-internal',
-    'bench/bench.c',
-    'bench/bench.h',
-    link_with: libxkbcommon_test_internal,
-)
-bench_dep = declare_dependency(
-    include_directories: include_directories('src'),
-    link_with: libxkbcommon_bench_internal,
-)
 bench_env = environment()
 bench_env.set('top_srcdir', meson.source_root())
 benchmark(
     'key-proc',
-    executable('bench-key-proc', 'bench/key-proc.c', dependencies: bench_dep),
+    executable('bench-key-proc', 'bench/key-proc.c', dependencies: test_dep),
     env: bench_env,
 )
 benchmark(
     'rules',
-    executable('bench-rules', 'bench/rules.c', dependencies: bench_dep),
+    executable('bench-rules', 'bench/rules.c', dependencies: test_dep),
     env: bench_env,
 )
 benchmark(
     'rulescomp',
-    executable('bench-rulescomp', 'bench/rulescomp.c', dependencies: bench_dep),
+    executable('bench-rulescomp', 'bench/rulescomp.c', dependencies: test_dep),
     env: bench_env,
 )
 benchmark(
     'compose',
-    executable('bench-compose', 'bench/compose.c', dependencies: bench_dep),
+    executable('bench-compose', 'bench/compose.c', dependencies: test_dep),
     env: bench_env,
 )
+if get_option('enable-x11')
+  benchmark(
+      'x11',
+      executable('bench-x11', 'bench/x11.c', dependencies: x11_test_dep),
+      env: bench_env,
+  )
+endif
 
 
 # Documentation.
diff --git a/src/context.c b/src/context.c
index 4a6ac8e..71c2275 100644
--- a/src/context.c
+++ b/src/context.c
@@ -210,6 +210,7 @@
     if (!ctx || --ctx->refcnt > 0)
         return;
 
+    free(ctx->x11_atom_cache);
     xkb_context_include_path_clear(ctx);
     atom_table_free(ctx->atom_table);
     free(ctx);
@@ -323,6 +324,8 @@
         return NULL;
     }
 
+    ctx->x11_atom_cache = NULL;
+
     return ctx;
 }
 
diff --git a/src/context.h b/src/context.h
index ead2508..44367cc 100644
--- a/src/context.h
+++ b/src/context.h
@@ -45,6 +45,9 @@
 
     struct atom_table *atom_table;
 
+    /* Used and allocated by xkbcommon-x11, free()d with the context. */
+    void *x11_atom_cache;
+
     /* Buffer for the *Text() functions. */
     char text_buffer[2048];
     size_t text_next;
diff --git a/src/x11/keymap.c b/src/x11/keymap.c
index 7369d5d..f5b368f 100644
--- a/src/x11/keymap.c
+++ b/src/x11/keymap.c
@@ -445,19 +445,19 @@
 
             FAIL_UNLESS((unsigned) syms_length == wire_sym_map->width * key->num_groups);
 
-            for (int j = 0; j < syms_length; j++) {
-                xcb_keysym_t wire_keysym = *syms_iter;
-                const xkb_layout_index_t group = j / wire_sym_map->width;
-                const xkb_level_index_t level = j % wire_sym_map->width;
+            for (xkb_layout_index_t group = 0; group < key->num_groups; group++) {
+                for (xkb_level_index_t level = 0; level < wire_sym_map->width; level++) {
+                    xcb_keysym_t wire_keysym = *syms_iter;
 
-                assert(key->groups[group].type != NULL);
-                if (level < key->groups[group].type->num_levels &&
-                    wire_keysym != XKB_KEY_NoSymbol) {
-                    key->groups[group].levels[level].num_syms = 1;
-                    key->groups[group].levels[level].u.sym = wire_keysym;
+                    assert(key->groups[group].type != NULL);
+                    if (level < key->groups[group].type->num_levels &&
+                        wire_keysym != XKB_KEY_NoSymbol) {
+                        key->groups[group].levels[level].num_syms = 1;
+                        key->groups[group].levels[level].u.sym = wire_keysym;
+                    }
+
+                    syms_iter++;
                 }
-
-                syms_iter++;
             }
         }
 
@@ -492,21 +492,23 @@
         uint8_t wire_count = *acts_count_iter;
         struct xkb_key *key = &keymap->keys[reply->firstKeyAction + i];
 
+        FAIL_UNLESS((unsigned) syms_length == wire_sym_map->width * key->num_groups);
         FAIL_UNLESS(wire_count == 0 || wire_count == syms_length);
 
-        for (int j = 0; j < wire_count; j++) {
-            xcb_xkb_action_t *wire_action = acts_iter.data;
-            const xkb_layout_index_t group = j / wire_sym_map->width;
-            const xkb_level_index_t level = j % wire_sym_map->width;
+        if (wire_count != 0) {
+            for (xkb_layout_index_t group = 0; group < key->num_groups; group++) {
+                for (xkb_level_index_t level = 0; level < wire_sym_map->width; level++) {
+                    xcb_xkb_action_t *wire_action = acts_iter.data;
 
-            if (level < key->groups[group].type->num_levels) {
-                union xkb_action *action =
-                    &key->groups[group].levels[level].action;
+                    if (level < key->groups[group].type->num_levels) {
+                        union xkb_action *action = &key->groups[group].levels[level].action;
 
-                translate_action(action, wire_action);
+                        translate_action(action, wire_action);
+                    }
+
+                    xcb_xkb_action_next(&acts_iter);
+                }
             }
-
-            xcb_xkb_action_next(&acts_iter);
         }
 
         acts_count_iter++;
@@ -886,6 +888,7 @@
                          wire_num_levels))
             goto fail;
 
+        type->num_level_names = type->num_levels;
         kt_level_names_iter += wire_num_levels;
         key_type_names_iter++;
         n_levels_per_type_iter++;
diff --git a/src/x11/util.c b/src/x11/util.c
index 3959a5a..660d885 100644
--- a/src/x11/util.c
+++ b/src/x11/util.c
@@ -155,6 +155,20 @@
     return true;
 }
 
+struct x11_atom_cache {
+    /*
+     * Invalidate the cache based on the XCB connection.
+     * X11 atoms are actually not per connection or client, but per X server
+     * session. But better be safe just in case we survive an X server restart.
+     */
+    xcb_connection_t *conn;
+    struct {
+        xcb_atom_t from;
+        xkb_atom_t to;
+    } cache[256];
+    size_t len;
+};
+
 bool
 adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn,
             const xcb_atom_t *from, xkb_atom_t *to, const size_t count)
@@ -163,24 +177,49 @@
     xcb_get_atom_name_cookie_t cookies[SIZE];
     const size_t num_batches = ROUNDUP(count, SIZE) / SIZE;
 
+    if (!ctx->x11_atom_cache) {
+        ctx->x11_atom_cache = calloc(1, sizeof(struct x11_atom_cache));
+    }
+    /* Can be NULL in case the malloc failed. */
+    struct x11_atom_cache *cache = ctx->x11_atom_cache;
+    if (cache && cache->conn != conn) {
+        cache->conn = conn;
+        cache->len = 0;
+    }
+
+    memset(to, 0, count * sizeof(*to));
+
     /* Send and collect the atoms in batches of reasonable SIZE. */
     for (size_t batch = 0; batch < num_batches; batch++) {
         const size_t start = batch * SIZE;
         const size_t stop = MIN((batch + 1) * SIZE, count);
 
         /* Send. */
-        for (size_t i = start; i < stop; i++)
-            if (from[i] != XCB_ATOM_NONE)
+        for (size_t i = start; i < stop; i++) {
+            bool cache_hit = false;
+            if (cache) {
+                for (size_t c = 0; c < cache->len; c++) {
+                    if (cache->cache[c].from == from[i]) {
+                        to[i] = cache->cache[c].to;
+                        cache_hit = true;
+                        break;
+                    }
+                }
+            }
+            if (!cache_hit && from[i] != XCB_ATOM_NONE)
                 cookies[i % SIZE] = xcb_get_atom_name(conn, from[i]);
+        }
 
         /* Collect. */
         for (size_t i = start; i < stop; i++) {
             xcb_get_atom_name_reply_t *reply;
 
-            if (from[i] == XCB_ATOM_NONE) {
-                to[i] = XKB_ATOM_NONE;
+            if (from[i] == XCB_ATOM_NONE)
                 continue;
-            }
+
+            /* Was filled from cache. */
+            if (to[i] != 0)
+                continue;
 
             reply = xcb_get_atom_name_reply(conn, cookies[i % SIZE], NULL);
             if (!reply)
@@ -194,6 +233,12 @@
             if (to[i] == XKB_ATOM_NONE)
                 goto err_discard;
 
+            if (cache && cache->len < ARRAY_SIZE(cache->cache)) {
+                size_t idx = cache->len++;
+                cache->cache[idx].from = from[i];
+                cache->cache[idx].to = to[i];
+            }
+
             continue;
 
             /*
diff --git a/src/xkbcomp/.gitignore b/src/xkbcomp/.gitignore
index e69de29..d7814e4 100644
--- a/src/xkbcomp/.gitignore
+++ b/src/xkbcomp/.gitignore
@@ -0,0 +1,2 @@
+parser.c
+parser.h
diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c
index e6b438a..b7828bf 100644
--- a/src/xkbcomp/keymap-dump.c
+++ b/src/xkbcomp/keymap-dump.c
@@ -277,9 +277,11 @@
 }
 
 static const char *
-affect_lock_text(enum xkb_action_flags flags)
+affect_lock_text(enum xkb_action_flags flags, bool show_both)
 {
     switch (flags & (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK)) {
+    case 0:
+        return show_both ? ",affect=both" : "";
     case ACTION_LOCK_NO_UNLOCK:
         return ",affect=lock";
     case ACTION_LOCK_NO_LOCK:
@@ -317,7 +319,7 @@
         write_buf(buf, "%s%s(modifiers=%s%s%s%s)%s", prefix, type, args,
                   (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "",
                   (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "",
-                  (action->type == ACTION_TYPE_MOD_LOCK) ? affect_lock_text(action->mods.flags) : "",
+                  (action->type == ACTION_TYPE_MOD_LOCK) ? affect_lock_text(action->mods.flags, false) : "",
                   suffix);
         break;
 
@@ -347,7 +349,7 @@
         break;
 
     case ACTION_TYPE_PTR_LOCK:
-        args = affect_lock_text(action->btn.flags);
+        args = affect_lock_text(action->btn.flags, true);
         /* fallthrough */
     case ACTION_TYPE_PTR_BUTTON:
         write_buf(buf, "%s%s(button=", prefix, type);
@@ -382,7 +384,7 @@
     case ACTION_TYPE_CTRL_LOCK:
         write_buf(buf, "%s%s(controls=%s%s)%s", prefix, type,
                   ControlMaskText(keymap->ctx, action->ctrls.ctrls),
-                  (action->type == ACTION_TYPE_CTRL_LOCK) ? affect_lock_text(action->ctrls.flags) : "",
+                  (action->type == ACTION_TYPE_CTRL_LOCK) ? affect_lock_text(action->ctrls.flags, false) : "",
                   suffix);
         break;
 
diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y
index 6dcb523..87dea65 100644
--- a/src/xkbcomp/parser.y
+++ b/src/xkbcomp/parser.y
@@ -726,8 +726,10 @@
 
 KeySym          :       IDENT
                         {
-                            if (!resolve_keysym($1, &$$))
+                            if (!resolve_keysym($1, &$$)) {
                                 parser_warn(param, "unrecognized keysym \"%s\"", $1);
+                                $$ = XKB_KEY_NoSymbol;
+                            }
                             free($1);
                         }
                 |       SECTION { $$ = XKB_KEY_section; }
diff --git a/test/data/keymaps/host.xkb b/test/data/keymaps/host.xkb
index c7d606f..dcaa677 100644
--- a/test/data/keymaps/host.xkb
+++ b/test/data/keymaps/host.xkb
@@ -300,300 +300,300 @@
 
 	type "ONE_LEVEL" {
 		modifiers= none;
-		level_name[Level1]= "Any";
+		level_name[1]= "Any";
 	};
 	type "TWO_LEVEL" {
 		modifiers= Shift;
-		map[Shift]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
+		map[Shift]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
 	};
 	type "ALPHABETIC" {
 		modifiers= Shift+Lock;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Caps";
+		map[Shift]= 2;
+		map[Lock]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Caps";
 	};
 	type "KEYPAD" {
 		modifiers= Shift+NumLock;
-		map[Shift]= Level2;
-		map[NumLock]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Number";
+		map[Shift]= 2;
+		map[NumLock]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Number";
 	};
 	type "SHIFT+ALT" {
 		modifiers= Shift+Alt;
-		map[Shift+Alt]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift+Alt";
+		map[Shift+Alt]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Shift+Alt";
 	};
 	type "PC_SUPER_LEVEL2" {
 		modifiers= Mod4;
-		map[Mod4]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Super";
+		map[Mod4]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Super";
 	};
 	type "PC_CONTROL_LEVEL2" {
 		modifiers= Control;
-		map[Control]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Control";
+		map[Control]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Control";
 	};
 	type "PC_LCONTROL_LEVEL2" {
 		modifiers= LControl;
-		map[LControl]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "LControl";
+		map[LControl]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "LControl";
 	};
 	type "PC_RCONTROL_LEVEL2" {
 		modifiers= RControl;
-		map[RControl]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "RControl";
+		map[RControl]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "RControl";
 	};
 	type "PC_ALT_LEVEL2" {
 		modifiers= Alt;
-		map[Alt]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Alt";
+		map[Alt]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "Alt";
 	};
 	type "PC_LALT_LEVEL2" {
 		modifiers= LAlt;
-		map[LAlt]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "LAlt";
+		map[LAlt]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "LAlt";
 	};
 	type "PC_RALT_LEVEL2" {
 		modifiers= RAlt;
-		map[RAlt]= Level2;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "RAlt";
+		map[RAlt]= 2;
+		level_name[1]= "Base";
+		level_name[2]= "RAlt";
 	};
 	type "CTRL+ALT" {
 		modifiers= Shift+Control+Alt+LevelThree;
-		map[Shift]= Level2;
+		map[Shift]= 2;
 		preserve[Shift]= Shift;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
 		preserve[Shift+LevelThree]= Shift;
-		map[Control+Alt]= Level5;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
-		level_name[Level5]= "Ctrl+Alt";
+		map[Control+Alt]= 5;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
+		level_name[5]= "Ctrl+Alt";
 	};
 	type "LOCAL_EIGHT_LEVEL" {
 		modifiers= Shift+Lock+Control+LevelThree;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+Lock+LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level4;
-		map[Control]= Level5;
-		map[Shift+Lock+Control]= Level5;
-		map[Shift+Control]= Level6;
-		map[Lock+Control]= Level6;
-		map[Control+LevelThree]= Level7;
-		map[Shift+Lock+Control+LevelThree]= Level7;
-		map[Shift+Control+LevelThree]= Level8;
-		map[Lock+Control+LevelThree]= Level8;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Level3";
-		level_name[Level4]= "Shift Level3";
-		level_name[Level5]= "Ctrl";
-		level_name[Level6]= "Shift Ctrl";
-		level_name[Level7]= "Level3 Ctrl";
-		level_name[Level8]= "Shift Level3 Ctrl";
+		map[Shift]= 2;
+		map[Lock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+Lock+LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 4;
+		map[Control]= 5;
+		map[Shift+Lock+Control]= 5;
+		map[Shift+Control]= 6;
+		map[Lock+Control]= 6;
+		map[Control+LevelThree]= 7;
+		map[Shift+Lock+Control+LevelThree]= 7;
+		map[Shift+Control+LevelThree]= 8;
+		map[Lock+Control+LevelThree]= 8;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "3";
+		level_name[4]= "Shift 3";
+		level_name[5]= "Ctrl";
+		level_name[6]= "Shift Ctrl";
+		level_name[7]= "3 Ctrl";
+		level_name[8]= "Shift 3 Ctrl";
 	};
 	type "THREE_LEVEL" {
 		modifiers= Shift+LevelThree;
-		map[Shift]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level3;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Level3";
+		map[Shift]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 3;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "3";
 	};
 	type "EIGHT_LEVEL" {
 		modifiers= Shift+LevelThree+LevelFive;
-		map[Shift]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[LevelFive]= Level5;
-		map[Shift+LevelFive]= Level6;
-		map[LevelThree+LevelFive]= Level7;
-		map[Shift+LevelThree+LevelFive]= Level8;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
-		level_name[Level5]= "X";
-		level_name[Level6]= "X Shift";
-		level_name[Level7]= "X Alt Base";
-		level_name[Level8]= "X Shift Alt";
+		map[Shift]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[LevelFive]= 5;
+		map[Shift+LevelFive]= 6;
+		map[LevelThree+LevelFive]= 7;
+		map[Shift+LevelThree+LevelFive]= 8;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
+		level_name[5]= "X";
+		level_name[6]= "X Shift";
+		level_name[7]= "X Alt Base";
+		level_name[8]= "X Shift Alt";
 	};
 	type "EIGHT_LEVEL_ALPHABETIC" {
 		modifiers= Shift+Lock+LevelThree+LevelFive;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level4;
-		map[Shift+Lock+LevelThree]= Level3;
-		map[LevelFive]= Level5;
-		map[Shift+LevelFive]= Level6;
-		map[Lock+LevelFive]= Level6;
-		map[LevelThree+LevelFive]= Level7;
-		map[Shift+LevelThree+LevelFive]= Level8;
-		map[Lock+LevelThree+LevelFive]= Level8;
-		map[Shift+Lock+LevelThree+LevelFive]= Level7;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
-		level_name[Level5]= "X";
-		level_name[Level6]= "X Shift";
-		level_name[Level7]= "X Alt Base";
-		level_name[Level8]= "X Shift Alt";
+		map[Shift]= 2;
+		map[Lock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 4;
+		map[Shift+Lock+LevelThree]= 3;
+		map[LevelFive]= 5;
+		map[Shift+LevelFive]= 6;
+		map[Lock+LevelFive]= 6;
+		map[LevelThree+LevelFive]= 7;
+		map[Shift+LevelThree+LevelFive]= 8;
+		map[Lock+LevelThree+LevelFive]= 8;
+		map[Shift+Lock+LevelThree+LevelFive]= 7;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
+		level_name[5]= "X";
+		level_name[6]= "X Shift";
+		level_name[7]= "X Alt Base";
+		level_name[8]= "X Shift Alt";
 	};
 	type "EIGHT_LEVEL_SEMIALPHABETIC" {
 		modifiers= Shift+Lock+LevelThree+LevelFive;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level3;
+		map[Shift]= 2;
+		map[Lock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 3;
 		preserve[Lock+LevelThree]= Lock;
-		map[Shift+Lock+LevelThree]= Level4;
+		map[Shift+Lock+LevelThree]= 4;
 		preserve[Shift+Lock+LevelThree]= Lock;
-		map[LevelFive]= Level5;
-		map[Shift+LevelFive]= Level6;
-		map[Lock+LevelFive]= Level6;
+		map[LevelFive]= 5;
+		map[Shift+LevelFive]= 6;
+		map[Lock+LevelFive]= 6;
 		preserve[Lock+LevelFive]= Lock;
-		map[Shift+Lock+LevelFive]= Level6;
+		map[Shift+Lock+LevelFive]= 6;
 		preserve[Shift+Lock+LevelFive]= Lock;
-		map[LevelThree+LevelFive]= Level7;
-		map[Shift+LevelThree+LevelFive]= Level8;
-		map[Lock+LevelThree+LevelFive]= Level7;
+		map[LevelThree+LevelFive]= 7;
+		map[Shift+LevelThree+LevelFive]= 8;
+		map[Lock+LevelThree+LevelFive]= 7;
 		preserve[Lock+LevelThree+LevelFive]= Lock;
-		map[Shift+Lock+LevelThree+LevelFive]= Level8;
+		map[Shift+Lock+LevelThree+LevelFive]= 8;
 		preserve[Shift+Lock+LevelThree+LevelFive]= Lock;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
-		level_name[Level5]= "X";
-		level_name[Level6]= "X Shift";
-		level_name[Level7]= "X Alt Base";
-		level_name[Level8]= "X Shift Alt";
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
+		level_name[5]= "X";
+		level_name[6]= "X Shift";
+		level_name[7]= "X Alt Base";
+		level_name[8]= "X Shift Alt";
 	};
 	type "FOUR_LEVEL" {
 		modifiers= Shift+LevelThree;
-		map[Shift]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
+		map[Shift]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
 	};
 	type "FOUR_LEVEL_ALPHABETIC" {
 		modifiers= Shift+Lock+LevelThree;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level4;
-		map[Shift+Lock+LevelThree]= Level3;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
+		map[Shift]= 2;
+		map[Lock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 4;
+		map[Shift+Lock+LevelThree]= 3;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
 	};
 	type "FOUR_LEVEL_SEMIALPHABETIC" {
 		modifiers= Shift+Lock+LevelThree;
-		map[Shift]= Level2;
-		map[Lock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level3;
+		map[Shift]= 2;
+		map[Lock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 3;
 		preserve[Lock+LevelThree]= Lock;
-		map[Shift+Lock+LevelThree]= Level4;
+		map[Shift+Lock+LevelThree]= 4;
 		preserve[Shift+Lock+LevelThree]= Lock;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
 	};
 	type "FOUR_LEVEL_MIXED_KEYPAD" {
 		modifiers= Shift+NumLock+LevelThree;
-		map[NumLock]= Level2;
-		map[Shift]= Level2;
-		map[LevelThree]= Level3;
-		map[NumLock+LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Shift+NumLock+LevelThree]= Level4;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Number";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
+		map[NumLock]= 2;
+		map[Shift]= 2;
+		map[LevelThree]= 3;
+		map[NumLock+LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Shift+NumLock+LevelThree]= 4;
+		level_name[1]= "Base";
+		level_name[2]= "Number";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
 	};
 	type "FOUR_LEVEL_X" {
 		modifiers= Shift+Control+Alt+LevelThree;
-		map[LevelThree]= Level2;
-		map[Shift+LevelThree]= Level3;
-		map[Control+Alt]= Level4;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Alt Base";
-		level_name[Level3]= "Shift Alt";
-		level_name[Level4]= "Ctrl+Alt";
+		map[LevelThree]= 2;
+		map[Shift+LevelThree]= 3;
+		map[Control+Alt]= 4;
+		level_name[1]= "Base";
+		level_name[2]= "Alt Base";
+		level_name[3]= "Shift Alt";
+		level_name[4]= "Ctrl+Alt";
 	};
 	type "SEPARATE_CAPS_AND_SHIFT_ALPHABETIC" {
 		modifiers= Shift+Lock+LevelThree;
-		map[Shift]= Level2;
-		map[Lock]= Level4;
+		map[Shift]= 2;
+		map[Lock]= 4;
 		preserve[Lock]= Lock;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock+LevelThree]= Level3;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock+LevelThree]= 3;
 		preserve[Lock+LevelThree]= Lock;
-		map[Shift+Lock+LevelThree]= Level3;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "AltGr Base";
-		level_name[Level4]= "Shift AltGr";
+		map[Shift+Lock+LevelThree]= 3;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "AltGr Base";
+		level_name[4]= "Shift AltGr";
 	};
 	type "FOUR_LEVEL_PLUS_LOCK" {
 		modifiers= Shift+Lock+LevelThree;
-		map[Shift]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[Lock]= Level5;
-		map[Shift+Lock]= Level2;
-		map[Lock+LevelThree]= Level3;
-		map[Shift+Lock+LevelThree]= Level4;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Shift";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Shift Alt";
-		level_name[Level5]= "Lock";
+		map[Shift]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[Lock]= 5;
+		map[Shift+Lock]= 2;
+		map[Lock+LevelThree]= 3;
+		map[Shift+Lock+LevelThree]= 4;
+		level_name[1]= "Base";
+		level_name[2]= "Shift";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Shift Alt";
+		level_name[5]= "Lock";
 	};
 	type "FOUR_LEVEL_KEYPAD" {
 		modifiers= Shift+NumLock+LevelThree;
-		map[Shift]= Level2;
-		map[NumLock]= Level2;
-		map[LevelThree]= Level3;
-		map[Shift+LevelThree]= Level4;
-		map[NumLock+LevelThree]= Level4;
-		map[Shift+NumLock+LevelThree]= Level3;
-		level_name[Level1]= "Base";
-		level_name[Level2]= "Number";
-		level_name[Level3]= "Alt Base";
-		level_name[Level4]= "Alt Number";
+		map[Shift]= 2;
+		map[NumLock]= 2;
+		map[LevelThree]= 3;
+		map[Shift+LevelThree]= 4;
+		map[NumLock+LevelThree]= 4;
+		map[Shift+NumLock+LevelThree]= 3;
+		level_name[1]= "Base";
+		level_name[2]= "Number";
+		level_name[3]= "Alt Base";
+		level_name[4]= "Alt Number";
 	};
 };
 
@@ -887,16 +887,16 @@
 		action= PtrBtn(button=3,count=2);
 	};
 	interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=default);
+		action= LockPtrBtn(button=default,affect=both);
 	};
 	interpret Pointer_Drag1+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=1);
+		action= LockPtrBtn(button=1,affect=both);
 	};
 	interpret Pointer_Drag2+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=2);
+		action= LockPtrBtn(button=2,affect=both);
 	};
 	interpret Pointer_Drag3+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=3);
+		action= LockPtrBtn(button=3,affect=both);
 	};
 	interpret Pointer_EnableKeys+AnyOfOrNone(all) {
 		action= LockControls(controls=MouseKeys);
@@ -1080,79 +1080,79 @@
 };
 
 xkb_symbols "pc_us_pt_2_us_3_inet(evdev)_group(shift_caps_toggle)_compose(ralt)" {
-	name[group1]="English (US)";
-	name[group2]="Portuguese";
-	name[group3]="English (US)";
+	name[Group1]="English (US)";
+	name[Group2]="Portuguese";
+	name[Group3]="English (US)";
 
 	key <ESC>                {	[          Escape ] };
 	key <AE01>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               1,          exclam ],
 		symbols[Group2]= [               1,          exclam,     onesuperior,      exclamdown ],
 		symbols[Group3]= [               1,          exclam ]
 	};
 	key <AE02>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               2,              at ],
 		symbols[Group2]= [               2,        quotedbl,              at,       oneeighth ],
 		symbols[Group3]= [               2,              at ]
 	};
 	key <AE03>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               3,      numbersign ],
 		symbols[Group2]= [               3,      numbersign,        sterling,        sterling ],
 		symbols[Group3]= [               3,      numbersign ]
 	};
 	key <AE04>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               4,          dollar ],
 		symbols[Group2]= [               4,          dollar,         section,          dollar ],
 		symbols[Group3]= [               4,          dollar ]
 	};
 	key <AE05>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               5,         percent ],
 		symbols[Group2]= [               5,         percent,         onehalf,    threeeighths ],
 		symbols[Group3]= [               5,         percent ]
 	};
 	key <AE06>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               6,     asciicircum ],
 		symbols[Group2]= [               6,       ampersand,         notsign,     fiveeighths ],
 		symbols[Group3]= [               6,     asciicircum ]
 	};
 	key <AE07>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               7,       ampersand ],
 		symbols[Group2]= [               7,           slash,       braceleft,    seveneighths ],
 		symbols[Group3]= [               7,       ampersand ]
 	};
 	key <AE08>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               8,        asterisk ],
 		symbols[Group2]= [               8,       parenleft,     bracketleft,       trademark ],
 		symbols[Group3]= [               8,        asterisk ]
 	};
 	key <AE09>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               9,       parenleft ],
 		symbols[Group2]= [               9,      parenright,    bracketright,       plusminus ],
 		symbols[Group3]= [               9,       parenleft ]
 	};
 	key <AE10>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [               0,      parenright ],
 		symbols[Group2]= [               0,           equal,      braceright,          degree ],
 		symbols[Group3]= [               0,      parenright ]
 	};
 	key <AE11>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [           minus,      underscore ],
 		symbols[Group2]= [      apostrophe,        question,       backslash,    questiondown ],
 		symbols[Group3]= [           minus,      underscore ]
 	};
 	key <AE12>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [           equal,            plus ],
 		symbols[Group2]= [   guillemotleft,  guillemotright,    dead_cedilla,     dead_ogonek ],
 		symbols[Group3]= [           equal,            plus ]
@@ -1160,93 +1160,93 @@
 	key <BKSP>               {	[       BackSpace,       BackSpace ] };
 	key <TAB>                {	[             Tab,    ISO_Left_Tab ] };
 	key <AD01>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               q,               Q ],
 		symbols[Group2]= [               q,               Q,              at,     Greek_OMEGA ],
 		symbols[Group3]= [               q,               Q ]
 	};
 	key <AD02>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               w,               W ],
 		symbols[Group2]= [               w,               W,         lstroke,         Lstroke ],
 		symbols[Group3]= [               w,               W ]
 	};
 	key <AD03>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               e,               E ],
 		symbols[Group2]= [               e,               E,        EuroSign,            cent ],
 		symbols[Group3]= [               e,               E ]
 	};
 	key <AD04>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               r,               R ],
 		symbols[Group2]= [               r,               R,       paragraph,      registered ],
 		symbols[Group3]= [               r,               R ]
 	};
 	key <AD05>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               t,               T ],
 		symbols[Group2]= [               t,               T,          tslash,          Tslash ],
 		symbols[Group3]= [               t,               T ]
 	};
 	key <AD06>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               y,               Y ],
 		symbols[Group2]= [               y,               Y,       leftarrow,             yen ],
 		symbols[Group3]= [               y,               Y ]
 	};
 	key <AD07>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               u,               U ],
 		symbols[Group2]= [               u,               U,       downarrow,         uparrow ],
 		symbols[Group3]= [               u,               U ]
 	};
 	key <AD08>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               i,               I ],
 		symbols[Group2]= [               i,               I,      rightarrow,        idotless ],
 		symbols[Group3]= [               i,               I ]
 	};
 	key <AD09>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               o,               O ],
 		symbols[Group2]= [               o,               O,          oslash,          Oslash ],
 		symbols[Group3]= [               o,               O ]
 	};
 	key <AD10>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               p,               P ],
 		symbols[Group2]= [               p,               P,           thorn,           THORN ],
 		symbols[Group3]= [               p,               P ]
 	};
 	key <AD11>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [     bracketleft,       braceleft ],
 		symbols[Group2]= [            plus,        asterisk,  dead_diaeresis,  dead_abovering ],
 		symbols[Group3]= [     bracketleft,       braceleft ]
 	};
 	key <AD12>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [    bracketright,      braceright ],
 		symbols[Group2]= [      dead_acute,      dead_grave,      dead_tilde,     dead_macron ],
 		symbols[Group3]= [    bracketright,      braceright ]
@@ -1254,172 +1254,172 @@
 	key <RTRN>               {	[          Return ] };
 	key <LCTL>               {	[       Control_L ] };
 	key <AC01>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               a,               A ],
 		symbols[Group2]= [               a,               A,              ae,              AE ],
 		symbols[Group3]= [               a,               A ]
 	};
 	key <AC02>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               s,               S ],
 		symbols[Group2]= [               s,               S,          ssharp,         section ],
 		symbols[Group3]= [               s,               S ]
 	};
 	key <AC03>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               d,               D ],
 		symbols[Group2]= [               d,               D,             eth,             ETH ],
 		symbols[Group3]= [               d,               D ]
 	};
 	key <AC04>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               f,               F ],
 		symbols[Group2]= [               f,               F,         dstroke,     ordfeminine ],
 		symbols[Group3]= [               f,               F ]
 	};
 	key <AC05>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               g,               G ],
 		symbols[Group2]= [               g,               G,             eng,             ENG ],
 		symbols[Group3]= [               g,               G ]
 	};
 	key <AC06>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               h,               H ],
 		symbols[Group2]= [               h,               H,         hstroke,         Hstroke ],
 		symbols[Group3]= [               h,               H ]
 	};
 	key <AC07>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               j,               J ],
 		symbols[Group2]= [               j,               J,       dead_hook,       dead_horn ],
 		symbols[Group3]= [               j,               J ]
 	};
 	key <AC08>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               k,               K ],
 		symbols[Group2]= [               k,               K,             kra,       ampersand ],
 		symbols[Group3]= [               k,               K ]
 	};
 	key <AC09>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               l,               L ],
 		symbols[Group2]= [               l,               L,         lstroke,         Lstroke ],
 		symbols[Group3]= [               l,               L ]
 	};
 	key <AC10>               {
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
 		symbols[Group1]= [       semicolon,           colon ],
 		symbols[Group2]= [        ccedilla,        Ccedilla,      dead_acute, dead_doubleacute ],
 		symbols[Group3]= [       semicolon,           colon ]
 	};
 	key <AC11>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [      apostrophe,        quotedbl ],
 		symbols[Group2]= [       masculine,     ordfeminine, dead_circumflex,      dead_caron ],
 		symbols[Group3]= [      apostrophe,        quotedbl ]
 	};
 	key <TLDE>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [           grave,      asciitilde ],
 		symbols[Group2]= [       backslash,             bar,         notsign,         notsign ],
 		symbols[Group3]= [           grave,      asciitilde ]
 	};
 	key <LFSH>               {	[         Shift_L ] };
 	key <BKSL>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [       backslash,             bar ],
 		symbols[Group2]= [      dead_tilde, dead_circumflex,      dead_grave,      dead_breve ],
 		symbols[Group3]= [       backslash,             bar ]
 	};
 	key <AB01>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               z,               Z ],
 		symbols[Group2]= [               z,               Z,   guillemotleft,            less ],
 		symbols[Group3]= [               z,               Z ]
 	};
 	key <AB02>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               x,               X ],
 		symbols[Group2]= [               x,               X,  guillemotright,         greater ],
 		symbols[Group3]= [               x,               X ]
 	};
 	key <AB03>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               c,               C ],
 		symbols[Group2]= [               c,               C,            cent,       copyright ],
 		symbols[Group3]= [               c,               C ]
 	};
 	key <AB04>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               v,               V ],
 		symbols[Group2]= [               v,               V, leftdoublequotemark, leftsinglequotemark ],
 		symbols[Group3]= [               v,               V ]
 	};
 	key <AB05>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               b,               B ],
 		symbols[Group2]= [               b,               B, rightdoublequotemark, rightsinglequotemark ],
 		symbols[Group3]= [               b,               B ]
 	};
 	key <AB06>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_ALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_ALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               n,               N ],
 		symbols[Group2]= [               n,               N,               n,               N ],
 		symbols[Group3]= [               n,               N ]
 	};
 	key <AB07>               {
-		type[group1]= "ALPHABETIC",
-		type[group2]= "FOUR_LEVEL_SEMIALPHABETIC",
-		type[group3]= "ALPHABETIC",
+		type[Group1]= "ALPHABETIC",
+		type[Group2]= "FOUR_LEVEL_SEMIALPHABETIC",
+		type[Group3]= "ALPHABETIC",
 		symbols[Group1]= [               m,               M ],
 		symbols[Group2]= [               m,               M,              mu,       masculine ],
 		symbols[Group3]= [               m,               M ]
 	};
 	key <AB08>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [           comma,            less ],
 		symbols[Group2]= [           comma,       semicolon,  horizconnector,        multiply ],
 		symbols[Group3]= [           comma,            less ]
 	};
 	key <AB09>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [          period,         greater ],
 		symbols[Group2]= [          period,           colon,  periodcentered,        division ],
 		symbols[Group3]= [          period,         greater ]
 	};
 	key <AB10>               {
-		type[group2]= "FOUR_LEVEL",
+		type[Group2]= "FOUR_LEVEL",
 		symbols[Group1]= [           slash,        question ],
 		symbols[Group2]= [           minus,      underscore,   dead_belowdot,   dead_abovedot ],
 		symbols[Group3]= [           slash,        question ]
@@ -1526,8 +1526,8 @@
 		symbols[Group1]= [           Print,         Sys_Req ]
 	};
 	key <RALT>               {
-		type[group1]= "TWO_LEVEL",
-		type[group2]= "ONE_LEVEL",
+		type[Group1]= "TWO_LEVEL",
+		type[Group2]= "ONE_LEVEL",
 		symbols[Group1]= [       Multi_key,       Multi_key ],
 		symbols[Group2]= [ ISO_Level3_Shift ]
 	};
diff --git a/test/data/keymaps/stringcomp.data b/test/data/keymaps/stringcomp.data
index 2fd27df..bc9b6ab 100644
--- a/test/data/keymaps/stringcomp.data
+++ b/test/data/keymaps/stringcomp.data
@@ -880,16 +880,16 @@
 		action= PtrBtn(button=3,count=2);
 	};
 	interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=default);
+		action= LockPtrBtn(button=default,affect=both);
 	};
 	interpret Pointer_Drag1+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=1);
+		action= LockPtrBtn(button=1,affect=both);
 	};
 	interpret Pointer_Drag2+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=2);
+		action= LockPtrBtn(button=2,affect=both);
 	};
 	interpret Pointer_Drag3+AnyOfOrNone(all) {
-		action= LockPtrBtn(button=3);
+		action= LockPtrBtn(button=3,affect=both);
 	};
 	interpret Pointer_EnableKeys+AnyOfOrNone(all) {
 		action= LockControls(controls=MouseKeys);
diff --git a/test/data/symbols/garbage b/test/data/symbols/garbage
new file mode 100644
index 0000000..98c5e28
--- /dev/null
+++ b/test/data/symbols/garbage
@@ -0,0 +1,14 @@
+default alphanumeric_keys
+xkb_symbols "garbage" {
+    include "us"
+
+    name[Group1]= "My garbage Layout";
+
+    // The garbage keysym will *not* override the corresponding symbol from the
+    // 'us' layout
+    key <TLDE> { [ keysym_is_garbage, exclam ] };
+
+    // AE13 is unused by 'us', use it to avoid fallback to the 'us' definition.
+    // Define with 2 levels but first level is a garbage symbol.
+    key <AE13> { [ keysym_is_garbage, asciitilde ] };
+};
diff --git a/test/keymap.c b/test/keymap.c
index a6bade8..816c2e4 100644
--- a/test/keymap.c
+++ b/test/keymap.c
@@ -31,8 +31,51 @@
 
 #include "test.h"
 
-int
-main(void)
+static void
+test_garbage_key(void)
+{
+    struct xkb_context *context = test_get_context(0);
+    struct xkb_keymap *keymap;
+    xkb_keycode_t kc;
+    int nsyms;
+    const xkb_keysym_t *syms;
+    const xkb_layout_index_t first_layout = 0;
+    xkb_level_index_t nlevels;
+
+    assert(context);
+
+    keymap = test_compile_rules(context, NULL, NULL, "garbage", NULL, NULL);
+    assert(keymap);
+
+    /* TLDE uses the 'us' sym on the first level and is thus [grave, exclam] */
+    kc = xkb_keymap_key_by_name(keymap, "TLDE");
+    assert(kc != XKB_KEYCODE_INVALID);
+    nlevels = xkb_keymap_num_levels_for_key(keymap, kc, first_layout);
+    assert(nlevels == 2);
+    nsyms = xkb_keymap_key_get_syms_by_level(keymap, kc, first_layout, 0, &syms);
+    assert(nsyms == 1);
+    assert(*syms == XKB_KEY_grave); /* fallback from 'us' */
+    nsyms = xkb_keymap_key_get_syms_by_level(keymap, kc, first_layout, 1, &syms);
+    assert(nsyms == 1);
+    assert(*syms == XKB_KEY_exclam);
+
+    /* AE13 has no 'us' fallback and ends up as [NoSymbol, asciitilde] */
+    kc = xkb_keymap_key_by_name(keymap, "AE13");
+    assert(kc != XKB_KEYCODE_INVALID);
+    nlevels = xkb_keymap_num_levels_for_key(keymap, kc, first_layout);
+    assert(nlevels == 2);
+    nsyms = xkb_keymap_key_get_syms_by_level(keymap, kc, first_layout, 0, &syms);
+    assert(nsyms == 0);
+    nsyms = xkb_keymap_key_get_syms_by_level(keymap, kc, first_layout, 1, &syms);
+    assert(nsyms == 1);
+    assert(*syms == XKB_KEY_asciitilde);
+
+    xkb_keymap_unref(keymap);
+    xkb_context_unref(context);
+}
+
+static void
+test_keymap(void)
 {
     struct xkb_context *context = test_get_context(0);
     struct xkb_keymap *keymap;
@@ -105,3 +148,12 @@
     xkb_keymap_unref(keymap);
     xkb_context_unref(context);
 }
+
+int
+main(void)
+{
+    test_garbage_key();
+    test_keymap();
+
+    return 0;
+}
diff --git a/test/xkeyboard-config-test.py.in b/test/xkeyboard-config-test.py.in
index ed37d7e..001f1b6 100755
--- a/test/xkeyboard-config-test.py.in
+++ b/test/xkeyboard-config-test.py.in
@@ -42,6 +42,7 @@
         o = rmlvo.get('o', None)
         args = [
             'xkbcli-compile-keymap',  # this is run in the builddir
+            '--verbose',
             '--rules', r,
             '--model', m,
             '--layout', l,
@@ -61,6 +62,12 @@
                                              universal_newlines=True)
             if verbose:
                 print(output, file=out)
+
+            if "unrecognized keysym" in output:
+                for line in output.split('\n'):
+                    if "unrecognized keysym" in line:
+                        print('ERROR: {}'.format(line))
+                success = False
         except subprocess.CalledProcessError as err:
             print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
             print(err.output, file=out)
diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h
index d6a02a7..ee7143c 100644
--- a/xkbcommon/xkbcommon.h
+++ b/xkbcommon/xkbcommon.h
@@ -208,6 +208,15 @@
  * Therefore, it is not safe to use the name as a unique identifier for a
  * layout.  Layout names are case-sensitive.
  *
+ * Layout names are specified in the layout's definition, for example
+ * "English (US)".  These are different from the (conventionally) short names
+ * which are used to locate the layout, for example "us" or "us(intl)".  These
+ * names are not present in a compiled keymap.
+ *
+ * If the user selects layouts from a list generated from the XKB registry
+ * (using libxkbregistry or directly), and this metadata is needed later on, it
+ * is recommended to store it along with the keymap.
+ *
  * Layouts are also called "groups" by XKB.
  *
  * @sa xkb_keymap_num_layouts() xkb_keymap_num_layouts_for_key()
@@ -1087,6 +1096,7 @@
  * a name, returns NULL.
  *
  * @sa xkb_layout_index_t
+ *     For notes on layout names.
  * @memberof xkb_keymap
  */
 const char *
@@ -1099,6 +1109,8 @@
  * XKB_LAYOUT_INVALID.  If more than one layout in the keymap has this name,
  * returns the lowest index among them.
  *
+ * @sa xkb_layout_index_t
+ *     For notes on layout names.
  * @memberof xkb_keymap
  */
 xkb_layout_index_t