···99// the start index of the next grapheme
1010idx: usize = 0,
11111212-/// the cache of graphemes. This allows up to 2048 graphemes with 4 codepoints
1313-/// each
1414-grapheme_buf: [1024 * 8 / 4]Grapheme = undefined,
1515-1616-// index of our next grapheme
1717-g_idx: u21 = 0,
1818-1919-pub const UNICODE_MAX = 1_114_112;
2020-2121-const Grapheme = struct {
2222- // codepoint is an index into the internal storage
2323- codepoint: u21,
2424- start: usize,
2525- end: usize,
2626-};
2727-2812/// put a slice of bytes in the cache as a grapheme
2929-pub fn put(self: *GraphemeCache, bytes: []const u8) !u21 {
3030- // See if we already have these bytes. It's a likely case that if we get one
3131- // grapheme, we'll get it again. So this will save a lot of storage and is
3232- // most likely worth the cost as it's pretty rare
3333- for (self.grapheme_buf) |grapheme| {
3434- const g_bytes = self.buf[grapheme.start..grapheme.end];
3535- if (std.mem.eql(u8, g_bytes, bytes)) {
3636- return grapheme.codepoint;
3737- }
3838- }
3939- if (self.idx + bytes.len > self.buf.len) return error.OutOfGraphemeBufferMemory;
4040- if (self.g_idx + 1 > self.grapheme_buf.len) return error.OutOfGraphemeMemory;
4141-1313+pub fn put(self: *GraphemeCache, bytes: []const u8) []u8 {
1414+ // reset the idx to 0 if we would overflow
1515+ if (self.idx + bytes.len > self.buf.len) self.idx = 0;
1616+ defer self.idx += bytes.len;
4217 // copy the grapheme to our storage
4318 @memcpy(self.buf[self.idx .. self.idx + bytes.len], bytes);
4444-4545- const g = Grapheme{
4646- // assign a codepoint that is always outside of valid unicode
4747- .codepoint = self.g_idx + UNICODE_MAX + 1,
4848- .start = self.idx,
4949- .end = self.idx + bytes.len,
5050- };
5151- self.grapheme_buf[self.g_idx] = g;
5252- self.g_idx += 1;
5353- self.idx += bytes.len;
5454-5555- return g.codepoint;
5656-}
5757-5858-/// get the slice of bytes for a given grapheme
5959-pub fn get(self: *GraphemeCache, cp: u21) ![]const u8 {
6060- if (cp < (UNICODE_MAX + 1)) return error.InvalidGraphemeIndex;
6161- const idx: usize = cp - UNICODE_MAX - 1;
6262- if (idx > self.g_idx) return error.InvalidGraphemeIndex;
6363- const g = self.grapheme_buf[idx];
6464- return self.buf[g.start..g.end];
6565-}
6666-6767-test "GraphemeCache: roundtrip" {
6868- var cache: GraphemeCache = .{};
6969- const cp = try cache.put("abc");
7070- const bytes = try cache.get(cp);
7171- try testing.expectEqualStrings("abc", bytes);
7272-7373- const cp_2 = try cache.put("abc");
7474- try testing.expectEqual(cp, cp_2);
7575-7676- const cp_3 = try cache.put("def");
7777- try testing.expectEqual(cp + 1, cp_3);
1919+ // return the slice
2020+ return self.buf[self.idx .. self.idx + bytes.len];
7821}
+8-3
src/Key.zig
···1414/// the unicode codepoint of the key event.
1515codepoint: u21,
16161717-/// the text generated from the key event. This will only contain a value if the
1818-/// event generated a multi-codepoint grapheme. If there was only a single
1919-/// codepoint, library users can encode the codepoint directly
1717+/// the text generated from the key event. The underlying slice has a limited
1818+/// lifetime. Vaxis maintains an internal ring buffer to temporarily store text.
1919+/// If the application needs these values longer than the lifetime of the event
2020+/// it must copy the data.
2021text: ?[]const u8 = null,
21222223/// the shifted codepoint of this key event. This will only be present if the
···3536pub const escape: u21 = 0x1B;
3637pub const space: u21 = 0x20;
3738pub const backspace: u21 = 0x7F;
3939+4040+// multicodepoint is a key which generated text but cannot be expressed as a
4141+// single codepoint. The value is the maximum unicode codepoint + 1
4242+pub const multicodepoint: u21 = 1_114_112 + 1;
38433944// kitty encodes these keys directly in the private use area. We reuse those
4045// mappings
+1-6
src/Tty.zig
···143143 switch (event) {
144144 .key_press => |key| {
145145 if (@hasField(EventType, "key_press")) {
146146- // HACK: yuck. there has to be a better way
147147- var mut_key = key;
148148- if (key.text) |text| {
149149- mut_key.codepoint = try vx.g_cache.put(text);
150150- }
151151- vx.postEvent(.{ .key_press = mut_key });
146146+ vx.postEvent(.{ .key_press = key });
152147 }
153148 },
154149 .focus_in => {