summaryrefslogtreecommitdiff
path: root/src/input/KeyEncoder.zig
diff options
context:
space:
mode:
authorMitchell Hashimoto <mitchell.hashimoto@gmail.com>2024-02-11 14:28:29 -0800
committerMitchell Hashimoto <mitchell.hashimoto@gmail.com>2024-02-11 14:28:53 -0800
commit2f36fd4b5fe8eaee60c5dcf20426fe7dbd24078a (patch)
tree742783daca2c33d63acd750f493f717b34842c4f /src/input/KeyEncoder.zig
parentdd7aff52f0658a9dd190f467516df46cf94f6b65 (diff)
input: ctrl ASCII uppercase behaves the same as ctrl lowercase
Fixes #1505 I verified this behavior with every other terminal and I've added test cases for it. We previously had a test case to assert the opposite, which is incorrect.
Diffstat (limited to 'src/input/KeyEncoder.zig')
-rw-r--r--src/input/KeyEncoder.zig53
1 files changed, 35 insertions, 18 deletions
diff --git a/src/input/KeyEncoder.zig b/src/input/KeyEncoder.zig
index 485029677..210a2219f 100644
--- a/src/input/KeyEncoder.zig
+++ b/src/input/KeyEncoder.zig
@@ -262,7 +262,7 @@ fn legacy(
// 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+<char>.
- if (ctrlSeq(self.event.utf8, all_mods)) |char| {
+ if (ctrlSeq(self.event.utf8, self.event.unshifted_codepoint, all_mods)) |char| {
// C0 sequences support alt-as-esc prefixing.
if (binding_mods.alt) {
if (buf.len < 2) return error.OutOfMemory;
@@ -498,16 +498,20 @@ fn pcStyleFunctionKey(
/// This will return null if the key event should not be converted
/// into a C0 byte. There are many cases for this and you should read
/// the source code to understand them.
-fn ctrlSeq(utf8: []const u8, mods: key.Mods) ?u8 {
+fn ctrlSeq(
+ utf8: []const u8,
+ unshifted_codepoint: u21,
+ mods: key.Mods,
+) ?u8 {
// If ctrl is not pressed then we never do anything.
if (!mods.ctrl) return null;
// If we don't have exactly one byte in our utf8 sequence, then
// we don't do anything, since all our ctrl keys are based on ASCII.
if (utf8.len != 1) return null;
- const char = utf8[0];
- const unset_mods = unset_mods: {
+ const char, const unset_mods = unset_mods: {
+ var char = utf8[0];
var unset_mods = mods;
// Remove alt from our modifiers because it does not impact whether
@@ -515,15 +519,23 @@ fn ctrlSeq(utf8: []const u8, mods: key.Mods) ?u8 {
// logic separately.
unset_mods.alt = false;
- // Remove shift if we have something outside of the US letter range
- if (unset_mods.shift and (char < 'A' or char > 'Z')) shift: {
+ // Remove shift because shifted characters are allowed to send
+ // control sequences. i.e. ctrl+A == ctrl+a
+ if (unset_mods.shift) shift: {
// Special case for fixterms awkward case as specified.
if (char == '@') break :shift;
-
unset_mods.shift = false;
}
- break :unset_mods unset_mods.binding();
+ // If the character is uppercase, we convert it to lowercase. We
+ // rely on the unshifted codepoint to do this.
+ if (char >= 'A' and char <= 'Z' and unshifted_codepoint > 0) {
+ if (std.math.cast(u8, unshifted_codepoint)) |byte| {
+ char = byte;
+ }
+ }
+
+ break :unset_mods .{ char, unset_mods.binding() };
};
// After unsetting, we only continue if we have ONLY control set.
@@ -1891,30 +1903,35 @@ test "legacy: right_shift+tab" {
}
test "ctrlseq: normal ctrl c" {
- const seq = ctrlSeq("c", .{ .ctrl = true });
+ const seq = ctrlSeq("c", 'c', .{ .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: normal ctrl c, right control" {
- const seq = ctrlSeq("c", .{ .ctrl = true, .sides = .{ .ctrl = .right } });
+ const seq = ctrlSeq("c", 'c', .{ .ctrl = true, .sides = .{ .ctrl = .right } });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: alt should be allowed" {
- const seq = ctrlSeq("c", .{ .alt = true, .ctrl = true });
+ const seq = ctrlSeq("c", 'c', .{ .alt = true, .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: no ctrl does nothing" {
- try testing.expect(ctrlSeq("c", .{}) == null);
-}
-
-test "ctrlseq: shift does not generate ctrl seq" {
- try testing.expect(ctrlSeq("C", .{ .shift = true }) == null);
- try testing.expect(ctrlSeq("C", .{ .shift = true, .ctrl = true }) == null);
+ try testing.expect(ctrlSeq("c", 'c', .{}) == null);
}
test "ctrlseq: shifted non-character" {
- const seq = ctrlSeq("_", .{ .ctrl = true, .shift = true });
+ const seq = ctrlSeq("_", '-', .{ .ctrl = true, .shift = true });
try testing.expectEqual(@as(u8, 0x1F), seq.?);
}
+
+test "ctrlseq: caps ascii letter" {
+ const seq = ctrlSeq("C", 'c', .{ .ctrl = true, .caps_lock = true });
+ try testing.expectEqual(@as(u8, 0x03), seq.?);
+}
+
+test "ctrlseq: shifted ascii letter" {
+ const seq = ctrlSeq("C", 'c', .{ .ctrl = true, .shift = true });
+ try testing.expectEqual(@as(u8, 0x03), seq.?);
+}