monstar

created pr with 137.1 on 2026-08-18T13:29:52Z · by c8ef7d19
cmds
checkout latest patchset:
ssh pr.pico.sh print 137 | git am -3
checkout any patchset in a patch request:
ssh pr.pico.sh print 137.[rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 137

Patchset 137.1 on 2026-08-18T13:29:52Z · commit 5d022b4

Under the Norwegian layout with grp:win_space_toggle,caps:escape, when the
keymap is loaded, physical keycode 58 (Caps Lock) is translated to .escape and
encoded as \x1b.

When the compositor sends a keymap buffer without a trailing null byte, the
size - 1 subtraction in Keyboard.zig truncates the closing syntax token, causing
xkb_keymap_new_from_buffer to fail with a syntax error.
Semantic diff summary
2 added, 0 modified, 0 signature changed, 0 removed across 1 analyzed file
+54 -0 src/Keyboard.zig #
......@@ -708,6 +708,60 @@ test "translate and encode: caps lock remapped to escape" {
708708 try std.testing.expectEqualStrings("\x1b", writer.buffered());
709709 }
710710
711+test "translate and encode: caps:escape with Norwegian layout and grp:win_space_toggle" {
712+ var kb: Keyboard = try .init();
713+ defer kb.deinit();
714+
715+ const names: c.xkb_rule_names = .{
716+ .rules = null,
717+ .model = null,
718+ .layout = "no",
719+ .variant = null,
720+ .options = "grp:win_space_toggle,caps:escape",
721+ };
722+ const keymap = c.xkb_keymap_new_from_names(kb.context, &names, c.XKB_KEYMAP_COMPILE_NO_FLAGS) orelse
723+ return error.KeymapParseFailed;
724+ try kb.installKeymap(keymap);
725+
726+ var utf8_buf: [16]u8 = undefined;
727+ const event = kb.translate(&utf8_buf, 58, .press).?;
728+ try std.testing.expectEqual(vt.input.Key.escape, event.key);
729+
730+ var out_buf: [64]u8 = undefined;
731+ var writer: std.Io.Writer = .fixed(&out_buf);
732+ try vt.input.encodeKey(&writer, event, .{});
733+ try std.testing.expectEqualStrings("\x1b", writer.buffered());
734+}
735+
736+test "keymap parsing fails when setKeymap size - 1 truncates keymap text" {
737+ var kb: Keyboard = try .init();
738+ defer kb.deinit();
739+
740+ // When a Wayland compositor (like Niri / Smithay) writes keymap text without a trailing NUL,
741+ // passing `size - 1` to xkb_keymap_new_from_buffer truncates the last byte of valid keymap text.
742+ const keymap_text = "xkb_keymap { xkb_keycodes { <ESC> = 9; }; xkb_types { }; xkb_compat { }; xkb_symbols { key <ESC> { [ Escape ] }; }; };";
743+
744+ const valid = c.xkb_keymap_new_from_buffer(
745+ kb.context,
746+ keymap_text.ptr,
747+ keymap_text.len,
748+ c.XKB_KEYMAP_FORMAT_TEXT_V1,
749+ c.XKB_KEYMAP_COMPILE_NO_FLAGS,
750+ );
751+ try std.testing.expect(valid != null);
752+ if (valid) |km| c.xkb_keymap_unref(km);
753+
754+ // Truncating with `size - 1` breaks syntax:
755+ const truncated = c.xkb_keymap_new_from_buffer(
756+ kb.context,
757+ keymap_text.ptr,
758+ keymap_text.len - 1,
759+ c.XKB_KEYMAP_FORMAT_TEXT_V1,
760+ c.XKB_KEYMAP_COMPILE_NO_FLAGS,
761+ );
762+ try std.testing.expect(truncated == null);
763+}
764+
711765 test "translate and encode: Kitty media key" {
712766 var kb = testKeyboard() catch return error.SkipZigTest;
713767 defer kb.deinit();
Back to top