From dcf9cdd8bfd9497ff2296cbc8fce843c18da633f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 16 Aug 2023 13:57:17 -0700 Subject: [PATCH] input: CSI u encoding for modified unicode chars --- src/input/KeyEncoder.zig | 101 ++++++++++++++++++++++++++++++--------- src/terminal/csi_u.zig | 64 ------------------------- src/terminal/main.zig | 1 - 3 files changed, 78 insertions(+), 88 deletions(-) delete mode 100644 src/terminal/csi_u.zig diff --git a/src/input/KeyEncoder.zig b/src/input/KeyEncoder.zig index 3ac8a5fc9..639cf9209 100644 --- a/src/input/KeyEncoder.zig +++ b/src/input/KeyEncoder.zig @@ -29,6 +29,7 @@ pub fn legacy( self: *const KeyEncoder, buf: []u8, ) ![]const u8 { + const all_mods = self.event.mods; const effective_mods = self.event.effectiveMods(); const binding_mods = effective_mods.binding(); @@ -48,8 +49,10 @@ pub fn legacy( self.modify_other_keys_state_2, )) |sequence| return copyToBuf(buf, sequence); - // If we match a control sequence, we output that directly. - if (ctrlSeq(self.event.key, binding_mods)) |char| { + // If we match a control sequence, we output that directly. For + // ctrlSeq we have to use all mods because we want it to only + // match ctrl+. + if (ctrlSeq(self.event.key, all_mods)) |char| { // C0 sequences support alt-as-esc prefixing. if (binding_mods.alt) { if (buf.len < 2) return error.OutOfMemory; @@ -113,26 +116,22 @@ pub fn legacy( } } - // // Let's see if we should apply fixterms to this codepoint. - // // At this stage of key processing, we only need to apply fixterms - // // to unicode codepoints (the point of charCallback) if we have - // // ctrl set. - // if (mods.ctrl) { - // const csi_u_mods = terminal.csi_u.Mods.fromInput(mods); - // const resp = try std.fmt.bufPrint( - // &data, - // "\x1B[{};{}u", - // .{ codepoint, csi_u_mods.seqInt() }, - // ); - // _ = self.io_thread.mailbox.push(.{ - // .write_small = .{ - // .data = data, - // .len = @intCast(resp.len), - // }, - // }, .{ .forever = {} }); - // try self.io_thread.wakeup.notify(); - // return; - // } + // Let's see if we should apply fixterms to this codepoint. + // At this stage of key processing, we only need to apply fixterms + // to unicode codepoints if we have ctrl set. + if (self.event.mods.ctrl) { + // Important: we want to use the original + const csi_u_mods = CsiUMods.fromInput(binding_mods); + const result = try std.fmt.bufPrint( + buf, + "\x1B[{};{}u", + .{ utf8[0], csi_u_mods.seqInt() }, + ); + + std.log.warn("CSI_U: {s}", .{result}); + + return result; + } // If we have alt-pressed and alt-esc-prefix is enabled, then // we need to prefix the utf8 sequence with an esc. @@ -223,7 +222,7 @@ fn ctrlSeq(keyval: key.Key, mods: key.Mods) ?u8 { const unalt_mods = unalt_mods: { var unalt_mods = mods; unalt_mods.alt = false; - break :unalt_mods unalt_mods; + break :unalt_mods unalt_mods.binding(); }; // If we have any other modifier key set, then we do not generate @@ -281,6 +280,62 @@ fn ctrlSeq(keyval: key.Key, mods: key.Mods) ?u8 { }; } +/// This is the bitmask for fixterm CSI u modifiers. +const CsiUMods = packed struct(u3) { + shift: bool = false, + alt: bool = false, + ctrl: bool = false, + + /// Convert an input mods value into the CSI u mods value. + pub fn fromInput(mods: key.Mods) CsiUMods { + return .{ + .shift = mods.shift, + .alt = mods.alt, + .ctrl = mods.ctrl, + }; + } + + /// Returns the raw int value of this packed struct. + pub fn int(self: CsiUMods) u3 { + return @bitCast(self); + } + + /// Returns the integer value sent as part of the CSI u sequence. + /// This adds 1 to the bitmask value as described in the spec. + pub fn seqInt(self: CsiUMods) u4 { + const raw: u4 = @intCast(self.int()); + return raw + 1; + } +}; + +test "modifer sequence values" { + // This is all sort of trivially seen by looking at the code but + // we want to make sure we never regress this. + var mods: CsiUMods = .{}; + try testing.expectEqual(@as(u4, 1), mods.seqInt()); + + mods = .{ .shift = true }; + try testing.expectEqual(@as(u4, 2), mods.seqInt()); + + mods = .{ .alt = true }; + try testing.expectEqual(@as(u4, 3), mods.seqInt()); + + mods = .{ .ctrl = true }; + try testing.expectEqual(@as(u4, 5), mods.seqInt()); + + mods = .{ .alt = true, .shift = true }; + try testing.expectEqual(@as(u4, 4), mods.seqInt()); + + mods = .{ .ctrl = true, .shift = true }; + try testing.expectEqual(@as(u4, 6), mods.seqInt()); + + mods = .{ .alt = true, .ctrl = true }; + try testing.expectEqual(@as(u4, 7), mods.seqInt()); + + mods = .{ .alt = true, .ctrl = true, .shift = true }; + try testing.expectEqual(@as(u4, 8), mods.seqInt()); +} + test "legacy: ctrl+alt+c" { var buf: [128]u8 = undefined; var enc: KeyEncoder = .{ diff --git a/src/terminal/csi_u.zig b/src/terminal/csi_u.zig deleted file mode 100644 index 93b1d1e7d..000000000 --- a/src/terminal/csi_u.zig +++ /dev/null @@ -1,64 +0,0 @@ -//! This file has information related to Paul Evans's "fixterms" -//! encoding, also sometimes referred to as "CSI u" encoding. -//! -//! https://www.leonerd.org.uk/hacks/fixterms/ - -const std = @import("std"); - -const input = @import("../input.zig"); - -pub const Mods = packed struct(u3) { - shift: bool = false, - alt: bool = false, - ctrl: bool = false, - - /// Convert an input mods value into the CSI u mods value. - pub fn fromInput(mods: input.Mods) Mods { - return .{ - .shift = mods.shift, - .alt = mods.alt, - .ctrl = mods.ctrl, - }; - } - - /// Returns the raw int value of this packed struct. - pub fn int(self: Mods) u3 { - return @bitCast(self); - } - - /// Returns the integer value sent as part of the CSI u sequence. - /// This adds 1 to the bitmask value as described in the spec. - pub fn seqInt(self: Mods) u4 { - const raw: u4 = @intCast(self.int()); - return raw + 1; - } -}; - -test "modifer sequence values" { - // This is all sort of trivially seen by looking at the code but - // we want to make sure we never regress this. - const testing = std.testing; - var mods: Mods = .{}; - try testing.expectEqual(@as(u4, 1), mods.seqInt()); - - mods = .{ .shift = true }; - try testing.expectEqual(@as(u4, 2), mods.seqInt()); - - mods = .{ .alt = true }; - try testing.expectEqual(@as(u4, 3), mods.seqInt()); - - mods = .{ .ctrl = true }; - try testing.expectEqual(@as(u4, 5), mods.seqInt()); - - mods = .{ .alt = true, .shift = true }; - try testing.expectEqual(@as(u4, 4), mods.seqInt()); - - mods = .{ .ctrl = true, .shift = true }; - try testing.expectEqual(@as(u4, 6), mods.seqInt()); - - mods = .{ .alt = true, .ctrl = true }; - try testing.expectEqual(@as(u4, 7), mods.seqInt()); - - mods = .{ .alt = true, .ctrl = true, .shift = true }; - try testing.expectEqual(@as(u4, 8), mods.seqInt()); -} diff --git a/src/terminal/main.zig b/src/terminal/main.zig index f08c97326..3de537656 100644 --- a/src/terminal/main.zig +++ b/src/terminal/main.zig @@ -7,7 +7,6 @@ const csi = @import("csi.zig"); const sgr = @import("sgr.zig"); pub const point = @import("point.zig"); pub const color = @import("color.zig"); -pub const csi_u = @import("csi_u.zig"); pub const modes = @import("modes.zig"); pub const parse_table = @import("parse_table.zig");