key: enable kitty keyboard
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
44ff960cb0
commit
aaa1c17a81
8 changed files with 114 additions and 29 deletions
|
@ -61,6 +61,8 @@ pub fn main() !void {
|
||||||
.winsize => |ws| {
|
.winsize => |ws| {
|
||||||
try vx.resize(alloc, ws);
|
try vx.resize(alloc, ws);
|
||||||
},
|
},
|
||||||
|
.cap_rgb => continue,
|
||||||
|
.cap_kitty_keyboard => try vx.enableKittyKeyboard(.{}),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,5 +98,7 @@ const Event = union(enum) {
|
||||||
key_press: vaxis.Key,
|
key_press: vaxis.Key,
|
||||||
winsize: vaxis.Winsize,
|
winsize: vaxis.Winsize,
|
||||||
focus_in,
|
focus_in,
|
||||||
|
cap_rgb,
|
||||||
|
cap_kitty_keyboard,
|
||||||
foo: u8,
|
foo: u8,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,14 @@ pub const Modifiers = packed struct(u8) {
|
||||||
num_lock: bool = false,
|
num_lock: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const KittyFlags = packed struct(u5) {
|
||||||
|
disambiguate: bool = true,
|
||||||
|
report_events: bool = false,
|
||||||
|
report_alternate_keys: bool = true,
|
||||||
|
report_all_as_ctl_seqs: bool = true,
|
||||||
|
report_text: bool = true,
|
||||||
|
};
|
||||||
|
|
||||||
/// the unicode codepoint of the key event.
|
/// the unicode codepoint of the key event.
|
||||||
codepoint: u21,
|
codepoint: u21,
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ const graphemeBreak = @import("ziglyph").graphemeBreak;
|
||||||
|
|
||||||
const log = std.log.scoped(.parser);
|
const log = std.log.scoped(.parser);
|
||||||
|
|
||||||
|
const Parser = @This();
|
||||||
|
|
||||||
/// The return type of our parse method. Contains an Event and the number of
|
/// The return type of our parse method. Contains an Event and the number of
|
||||||
/// bytes read from the buffer.
|
/// bytes read from the buffer.
|
||||||
pub const Result = struct {
|
pub const Result = struct {
|
||||||
|
@ -44,7 +46,11 @@ const State = enum {
|
||||||
ss3,
|
ss3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse(input: []const u8) !Result {
|
// a buffer to temporarily store text in. We need this to encode
|
||||||
|
// text-as-codepoints
|
||||||
|
buf: [128]u8 = undefined,
|
||||||
|
|
||||||
|
pub fn parse(self: *Parser, input: []const u8) !Result {
|
||||||
const n = input.len;
|
const n = input.len;
|
||||||
|
|
||||||
var seq: Sequence = .{};
|
var seq: Sequence = .{};
|
||||||
|
@ -349,10 +355,26 @@ pub fn parse(input: []const u8) !Result {
|
||||||
key.base_layout_codepoint = seq.params[idx];
|
key.base_layout_codepoint = seq.params[idx];
|
||||||
},
|
},
|
||||||
1 => {
|
1 => {
|
||||||
|
defer field += 1;
|
||||||
// field 1 is modifiers and optionally
|
// field 1 is modifiers and optionally
|
||||||
// the event type (csiu)
|
// the event type (csiu). It can be empty
|
||||||
const mod_mask: u8 = @truncate(seq.params[idx] - 1);
|
if (seq.empty_state.isSet(idx)) {
|
||||||
key.mods = @bitCast(mod_mask);
|
continue;
|
||||||
|
}
|
||||||
|
// default of 1
|
||||||
|
const ps: u8 = blk: {
|
||||||
|
if (seq.params[idx] == 0) break :blk 1;
|
||||||
|
break :blk @truncate(seq.params[idx]);
|
||||||
|
};
|
||||||
|
key.mods = @bitCast(ps - 1);
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
// field 2 is text, as codepoints
|
||||||
|
var total: usize = 0;
|
||||||
|
while (idx < seq.param_idx) : (idx += 1) {
|
||||||
|
total += try std.unicode.utf8Encode(seq.params[idx], self.buf[total..]);
|
||||||
|
}
|
||||||
|
key.text = self.buf[0..total];
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
@ -377,7 +399,8 @@ pub fn parse(input: []const u8) !Result {
|
||||||
|
|
||||||
test "parse: single xterm keypress" {
|
test "parse: single xterm keypress" {
|
||||||
const input = "a";
|
const input = "a";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 'a',
|
.codepoint = 'a',
|
||||||
.text = "a",
|
.text = "a",
|
||||||
|
@ -390,7 +413,8 @@ test "parse: single xterm keypress" {
|
||||||
|
|
||||||
test "parse: single xterm keypress with more buffer" {
|
test "parse: single xterm keypress with more buffer" {
|
||||||
const input = "ab";
|
const input = "ab";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 'a',
|
.codepoint = 'a',
|
||||||
.text = "a",
|
.text = "a",
|
||||||
|
@ -404,7 +428,8 @@ test "parse: single xterm keypress with more buffer" {
|
||||||
|
|
||||||
test "parse: xterm escape keypress" {
|
test "parse: xterm escape keypress" {
|
||||||
const input = "\x1b";
|
const input = "\x1b";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = Key.escape };
|
const expected_key: Key = .{ .codepoint = Key.escape };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -414,7 +439,8 @@ test "parse: xterm escape keypress" {
|
||||||
|
|
||||||
test "parse: xterm ctrl+a" {
|
test "parse: xterm ctrl+a" {
|
||||||
const input = "\x01";
|
const input = "\x01";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .ctrl = true } };
|
const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .ctrl = true } };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -424,7 +450,8 @@ test "parse: xterm ctrl+a" {
|
||||||
|
|
||||||
test "parse: xterm alt+a" {
|
test "parse: xterm alt+a" {
|
||||||
const input = "\x1ba";
|
const input = "\x1ba";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .alt = true } };
|
const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .alt = true } };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -434,7 +461,8 @@ test "parse: xterm alt+a" {
|
||||||
|
|
||||||
test "parse: xterm invalid ss3" {
|
test "parse: xterm invalid ss3" {
|
||||||
const input = "\x1bOZ";
|
const input = "\x1bOZ";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
|
|
||||||
try testing.expectEqual(3, result.n);
|
try testing.expectEqual(3, result.n);
|
||||||
try testing.expectEqual(null, result.event);
|
try testing.expectEqual(null, result.event);
|
||||||
|
@ -444,7 +472,8 @@ test "parse: xterm key up" {
|
||||||
{
|
{
|
||||||
// normal version
|
// normal version
|
||||||
const input = "\x1bOA";
|
const input = "\x1bOA";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = Key.up };
|
const expected_key: Key = .{ .codepoint = Key.up };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -455,7 +484,8 @@ test "parse: xterm key up" {
|
||||||
{
|
{
|
||||||
// application keys version
|
// application keys version
|
||||||
const input = "\x1b[2~";
|
const input = "\x1b[2~";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = Key.insert };
|
const expected_key: Key = .{ .codepoint = Key.insert };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -466,7 +496,8 @@ test "parse: xterm key up" {
|
||||||
|
|
||||||
test "parse: xterm shift+up" {
|
test "parse: xterm shift+up" {
|
||||||
const input = "\x1b[1;2A";
|
const input = "\x1b[1;2A";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } };
|
const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -476,7 +507,8 @@ test "parse: xterm shift+up" {
|
||||||
|
|
||||||
test "parse: xterm insert" {
|
test "parse: xterm insert" {
|
||||||
const input = "\x1b[1;2A";
|
const input = "\x1b[1;2A";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } };
|
const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } };
|
||||||
const expected_event: Event = .{ .key_press = expected_key };
|
const expected_event: Event = .{ .key_press = expected_key };
|
||||||
|
|
||||||
|
@ -486,7 +518,8 @@ test "parse: xterm insert" {
|
||||||
|
|
||||||
test "parse: paste_start" {
|
test "parse: paste_start" {
|
||||||
const input = "\x1b[200~";
|
const input = "\x1b[200~";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_event: Event = .paste_start;
|
const expected_event: Event = .paste_start;
|
||||||
|
|
||||||
try testing.expectEqual(6, result.n);
|
try testing.expectEqual(6, result.n);
|
||||||
|
@ -495,7 +528,8 @@ test "parse: paste_start" {
|
||||||
|
|
||||||
test "parse: paste_end" {
|
test "parse: paste_end" {
|
||||||
const input = "\x1b[201~";
|
const input = "\x1b[201~";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_event: Event = .paste_end;
|
const expected_event: Event = .paste_end;
|
||||||
|
|
||||||
try testing.expectEqual(6, result.n);
|
try testing.expectEqual(6, result.n);
|
||||||
|
@ -504,7 +538,8 @@ test "parse: paste_end" {
|
||||||
|
|
||||||
test "parse: focus_in" {
|
test "parse: focus_in" {
|
||||||
const input = "\x1b[I";
|
const input = "\x1b[I";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_event: Event = .focus_in;
|
const expected_event: Event = .focus_in;
|
||||||
|
|
||||||
try testing.expectEqual(3, result.n);
|
try testing.expectEqual(3, result.n);
|
||||||
|
@ -513,7 +548,8 @@ test "parse: focus_in" {
|
||||||
|
|
||||||
test "parse: focus_out" {
|
test "parse: focus_out" {
|
||||||
const input = "\x1b[O";
|
const input = "\x1b[O";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_event: Event = .focus_out;
|
const expected_event: Event = .focus_out;
|
||||||
|
|
||||||
try testing.expectEqual(3, result.n);
|
try testing.expectEqual(3, result.n);
|
||||||
|
@ -522,7 +558,8 @@ test "parse: focus_out" {
|
||||||
|
|
||||||
test "parse: kitty: shift+a without text reporting" {
|
test "parse: kitty: shift+a without text reporting" {
|
||||||
const input = "\x1b[97:65;2u";
|
const input = "\x1b[97:65;2u";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 'a',
|
.codepoint = 'a',
|
||||||
.shifted_codepoint = 'A',
|
.shifted_codepoint = 'A',
|
||||||
|
@ -536,7 +573,8 @@ test "parse: kitty: shift+a without text reporting" {
|
||||||
|
|
||||||
test "parse: kitty: alt+shift+a without text reporting" {
|
test "parse: kitty: alt+shift+a without text reporting" {
|
||||||
const input = "\x1b[97:65;4u";
|
const input = "\x1b[97:65;4u";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 'a',
|
.codepoint = 'a',
|
||||||
.shifted_codepoint = 'A',
|
.shifted_codepoint = 'A',
|
||||||
|
@ -550,7 +588,8 @@ test "parse: kitty: alt+shift+a without text reporting" {
|
||||||
|
|
||||||
test "parse: kitty: a without text reporting" {
|
test "parse: kitty: a without text reporting" {
|
||||||
const input = "\x1b[97u";
|
const input = "\x1b[97u";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 'a',
|
.codepoint = 'a',
|
||||||
};
|
};
|
||||||
|
@ -562,7 +601,8 @@ test "parse: kitty: a without text reporting" {
|
||||||
|
|
||||||
test "parse: single codepoint" {
|
test "parse: single codepoint" {
|
||||||
const input = "🙂";
|
const input = "🙂";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 0x1F642,
|
.codepoint = 0x1F642,
|
||||||
.text = input,
|
.text = input,
|
||||||
|
@ -575,7 +615,8 @@ test "parse: single codepoint" {
|
||||||
|
|
||||||
test "parse: single codepoint with more in buffer" {
|
test "parse: single codepoint with more in buffer" {
|
||||||
const input = "🙂a";
|
const input = "🙂a";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = 0x1F642,
|
.codepoint = 0x1F642,
|
||||||
.text = "🙂",
|
.text = "🙂",
|
||||||
|
@ -590,7 +631,8 @@ test "parse: multiple codepoint grapheme" {
|
||||||
// TODO: this test is passing but throws a warning. Not sure how we'll
|
// TODO: this test is passing but throws a warning. Not sure how we'll
|
||||||
// handle graphemes yet
|
// handle graphemes yet
|
||||||
const input = "👩🚀";
|
const input = "👩🚀";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = Key.multicodepoint,
|
.codepoint = Key.multicodepoint,
|
||||||
.text = input,
|
.text = input,
|
||||||
|
@ -605,7 +647,8 @@ test "parse: multiple codepoint grapheme with more after" {
|
||||||
// TODO: this test is passing but throws a warning. Not sure how we'll
|
// TODO: this test is passing but throws a warning. Not sure how we'll
|
||||||
// handle graphemes yet
|
// handle graphemes yet
|
||||||
const input = "👩🚀abc";
|
const input = "👩🚀abc";
|
||||||
const result = try parse(input);
|
var parser: Parser = .{};
|
||||||
|
const result = try parser.parse(input);
|
||||||
const expected_key: Key = .{
|
const expected_key: Key = .{
|
||||||
.codepoint = Key.multicodepoint,
|
.codepoint = Key.multicodepoint,
|
||||||
.text = "👩🚀",
|
.text = "👩🚀",
|
|
@ -4,7 +4,7 @@ const os = std.os;
|
||||||
const vaxis = @import("main.zig");
|
const vaxis = @import("main.zig");
|
||||||
const Vaxis = vaxis.Vaxis;
|
const Vaxis = vaxis.Vaxis;
|
||||||
const Event = @import("event.zig").Event;
|
const Event = @import("event.zig").Event;
|
||||||
const parser = @import("parser.zig");
|
const Parser = @import("Parser.zig");
|
||||||
const Key = vaxis.Key;
|
const Key = vaxis.Key;
|
||||||
const GraphemeCache = @import("GraphemeCache.zig");
|
const GraphemeCache = @import("GraphemeCache.zig");
|
||||||
|
|
||||||
|
@ -122,6 +122,8 @@ pub fn run(
|
||||||
.{ .fd = pipe[0], .events = std.os.POLL.IN, .revents = undefined },
|
.{ .fd = pipe[0], .events = std.os.POLL.IN, .revents = undefined },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var parser: Parser = .{};
|
||||||
|
|
||||||
// initialize the read buffer
|
// initialize the read buffer
|
||||||
var buf: [1024]u8 = undefined;
|
var buf: [1024]u8 = undefined;
|
||||||
// read loop
|
// read loop
|
||||||
|
@ -180,6 +182,11 @@ pub fn run(
|
||||||
vx.postEvent(.cap_kitty_keyboard);
|
vx.postEvent(.cap_kitty_keyboard);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.cap_rgb => {
|
||||||
|
if (@hasField(EventType, "cap_rgb")) {
|
||||||
|
vx.postEvent(.cap_rgb);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,8 @@ pub const Event = union(enum) {
|
||||||
focus_out,
|
focus_out,
|
||||||
paste_start,
|
paste_start,
|
||||||
paste_end,
|
paste_end,
|
||||||
|
|
||||||
|
// these are delivered as discovered terminal capabilities
|
||||||
cap_kitty_keyboard,
|
cap_kitty_keyboard,
|
||||||
|
cap_rgb,
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@ test {
|
||||||
_ = @import("GraphemeCache.zig");
|
_ = @import("GraphemeCache.zig");
|
||||||
_ = @import("Key.zig");
|
_ = @import("Key.zig");
|
||||||
_ = @import("Options.zig");
|
_ = @import("Options.zig");
|
||||||
|
_ = @import("Parser.zig");
|
||||||
_ = @import("Screen.zig");
|
_ = @import("Screen.zig");
|
||||||
_ = @import("Tty.zig");
|
_ = @import("Tty.zig");
|
||||||
_ = @import("Window.zig");
|
_ = @import("Window.zig");
|
||||||
|
@ -27,6 +28,5 @@ test {
|
||||||
_ = @import("ctlseqs.zig");
|
_ = @import("ctlseqs.zig");
|
||||||
_ = @import("event.zig");
|
_ = @import("event.zig");
|
||||||
_ = @import("queue.zig");
|
_ = @import("queue.zig");
|
||||||
_ = @import("parser.zig");
|
|
||||||
_ = @import("vaxis.zig");
|
_ = @import("vaxis.zig");
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,9 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
/// alt_screen state. We track so we can exit on deinit
|
/// alt_screen state. We track so we can exit on deinit
|
||||||
alt_screen: bool,
|
alt_screen: bool,
|
||||||
|
|
||||||
|
/// if we have entered kitty keyboard
|
||||||
|
kitty_keyboard: bool = false,
|
||||||
|
|
||||||
/// if we should redraw the entire screen on the next render
|
/// if we should redraw the entire screen on the next render
|
||||||
refresh: bool = false,
|
refresh: bool = false,
|
||||||
|
|
||||||
|
@ -74,6 +77,10 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
_ = tty.write(ctlseqs.rmcup) catch {};
|
_ = tty.write(ctlseqs.rmcup) catch {};
|
||||||
tty.flush() catch {};
|
tty.flush() catch {};
|
||||||
}
|
}
|
||||||
|
if (self.kitty_keyboard) {
|
||||||
|
_ = tty.write(ctlseqs.csi_u_pop) catch {};
|
||||||
|
tty.flush() catch {};
|
||||||
|
}
|
||||||
tty.deinit();
|
tty.deinit();
|
||||||
}
|
}
|
||||||
if (alloc) |a| {
|
if (alloc) |a| {
|
||||||
|
@ -169,7 +176,9 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
if (std.mem.eql(u8, colorterm, "truecolor") or
|
if (std.mem.eql(u8, colorterm, "truecolor") or
|
||||||
std.mem.eql(u8, colorterm, "24bit"))
|
std.mem.eql(u8, colorterm, "24bit"))
|
||||||
{
|
{
|
||||||
// TODO: Notify rgb support
|
if (@hasField(EventType, "cap_rgb")) {
|
||||||
|
self.postEvent(.cap_rgb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: decide if we actually want to query for focus and sync. It
|
// TODO: decide if we actually want to query for focus and sync. It
|
||||||
|
@ -411,6 +420,17 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
_ = try tty.write(ctlseqs.show_cursor);
|
_ = try tty.write(ctlseqs.show_cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn enableKittyKeyboard(self: *Self, flags: Key.KittyFlags) !void {
|
||||||
|
const flag_int: u5 = @bitCast(flags);
|
||||||
|
try std.fmt.format(
|
||||||
|
self.tty.?.buffered_writer.writer(),
|
||||||
|
ctlseqs.csi_u_push,
|
||||||
|
.{
|
||||||
|
flag_int,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub fn draw(self: *TextInput, win: Window) void {
|
||||||
var cursor_idx: usize = 0;
|
var cursor_idx: usize = 0;
|
||||||
while (iter.next()) |grapheme| {
|
while (iter.next()) |grapheme| {
|
||||||
const g = grapheme.slice(self.buf.items);
|
const g = grapheme.slice(self.buf.items);
|
||||||
const w = strWidth(g, .full) catch 1;
|
const w = strWidth(g, .half) catch 1;
|
||||||
win.writeCell(col, 0, .{
|
win.writeCell(col, 0, .{
|
||||||
.char = .{
|
.char = .{
|
||||||
.grapheme = g,
|
.grapheme = g,
|
||||||
|
|
Loading…
Reference in a new issue