lib/xkb/src/keysym/unicode.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const keysym = @import("root.zig");
 3 const data = @import("generated/root.zig").unicode;
 4 
 5 const Keysym = keysym.Keysym;
 6 
 7 pub fn codepoint(value: Keysym) ?u21 {
 8     const raw = @backingInt(value);
 9     if (raw >= keysym.unicode_offset and raw <= keysym.unicode_max) {
10         const direct = raw - keysym.unicode_offset;
11         if (direct == 0 or isSurrogate(direct)) return null;
12         return @intCast(direct);
13     }
14     var low: usize = 0;
15     var high: usize = data.entries.len;
16     while (low < high) {
17         const middle = low + (high - low) / 2;
18         const entry = data.entries[middle];
19         if (raw < entry.keysym) {
20             high = middle;
21         } else if (raw > entry.keysym) {
22             low = middle + 1;
23         } else {
24             return entry.codepoint;
25         }
26     }
27     return null;
28 }
29 
30 fn isSurrogate(value: u32) bool {
31     return value >= 0xd800 and value <= 0xdfff;
32 }
33 
34 test "every pinned keysym Unicode mapping resolves" {
35     try std.testing.expectEqual(@as(usize, data.count), data.entries.len);
36     for (data.entries) |entry| {
37         try std.testing.expectEqual(@as(?u21, entry.codepoint), codepoint(@fromBackingInt(@intCast(entry.keysym))));
38     }
39 }
40 
41 test "legacy special and direct keysyms resolve to Unicode scalars" {
42     const cases = [_]struct { value: u32, expected: ?u21 }{
43         .{ .value = 0x000001a1, .expected = 0x0104 },
44         .{ .value = 0x0000ff08, .expected = 0x0008 },
45         .{ .value = 0x0000ffb7, .expected = 0x0037 },
46         .{ .value = 0x10081200, .expected = 0x0030 },
47         .{ .value = 0x1008120a, .expected = 0x002a },
48         .{ .value = 0x01000080, .expected = 0x0080 },
49         .{ .value = 0x0101f600, .expected = 0x1f600 },
50         .{ .value = 0x0110ffff, .expected = 0x10ffff },
51         .{ .value = 0x0000ffff, .expected = 0x007f },
52         .{ .value = 0x0000ffbe, .expected = null },
53         .{ .value = 0x01000000, .expected = null },
54         .{ .value = 0x0100d800, .expected = null },
55         .{ .value = 0x0100dfff, .expected = null },
56         .{ .value = 0x01110000, .expected = null },
57     };
58     for (cases) |case| {
59         try std.testing.expectEqual(case.expected, codepoint(@fromBackingInt(@intCast(case.value))));
60     }
61 }