mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-17 07:16:12 +00:00
input: w3c names for keys
This commit is contained in:
@@ -1174,6 +1174,12 @@ pub const Trigger = struct {
|
||||
continue :loop;
|
||||
}
|
||||
|
||||
// Look for a matching w3c name next.
|
||||
if (key.Key.fromW3C(part)) |w3c_key| {
|
||||
result.key = .{ .physical = w3c_key };
|
||||
continue :loop;
|
||||
}
|
||||
|
||||
// If we're still unset then we look for backwards compatible
|
||||
// keys with Ghostty 1.1.x. We do this last so its least likely
|
||||
// to impact performance for modern users.
|
||||
@@ -1938,6 +1944,22 @@ test "parse: triggers" {
|
||||
try testing.expectError(Error.InvalidFormat, parseSingle("a+b=ignore"));
|
||||
}
|
||||
|
||||
test "parse: w3c key names" {
|
||||
const testing = std.testing;
|
||||
|
||||
// Exact match
|
||||
try testing.expectEqual(
|
||||
Binding{
|
||||
.trigger = .{ .key = .{ .physical = .key_a } },
|
||||
.action = .{ .ignore = {} },
|
||||
},
|
||||
try parseSingle("KeyA=ignore"),
|
||||
);
|
||||
|
||||
// Case-sensitive
|
||||
try testing.expectError(Error.InvalidFormat, parseSingle("Keya=ignore"));
|
||||
}
|
||||
|
||||
// For Ghostty 1.2+ we changed our key names to match the W3C and removed
|
||||
// `physical:`. This tests the backwards compatibility with the old format.
|
||||
// Note that our backwards compatibility isn't 100% perfect since triggers
|
||||
|
@@ -497,6 +497,74 @@ pub const Key = enum(c_int) {
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts a W3C key code to a Ghostty key enum value.
|
||||
///
|
||||
/// All required W3C key codes are supported, but there are a number of
|
||||
/// non-standard key codes that are not supported. In the case the value is
|
||||
/// invalid or unsupported, this function will return null.
|
||||
pub fn fromW3C(code: []const u8) ?Key {
|
||||
var result: [128]u8 = undefined;
|
||||
|
||||
// If the code is bigger than our buffer it can't possibly match.
|
||||
if (code.len > result.len) return null;
|
||||
|
||||
// First just check the whole thing lowercased, this is the simple case
|
||||
if (std.meta.stringToEnum(
|
||||
Key,
|
||||
std.ascii.lowerString(&result, code),
|
||||
)) |key| return key;
|
||||
|
||||
// We need to convert FooBar to foo_bar
|
||||
var fbs = std.io.fixedBufferStream(&result);
|
||||
const w = fbs.writer();
|
||||
for (code, 0..) |ch, i| switch (ch) {
|
||||
'a'...'z' => w.writeByte(ch) catch return null,
|
||||
|
||||
// Caps and numbers trigger underscores
|
||||
'A'...'Z', '0'...'9' => {
|
||||
if (i > 0) w.writeByte('_') catch return null;
|
||||
w.writeByte(std.ascii.toLower(ch)) catch return null;
|
||||
},
|
||||
|
||||
// We don't know of any key codes that aren't alphanumeric.
|
||||
else => return null,
|
||||
};
|
||||
|
||||
return std.meta.stringToEnum(Key, fbs.getWritten());
|
||||
}
|
||||
|
||||
/// Converts a Ghostty key enum value to a W3C key code.
|
||||
pub fn w3c(self: Key) []const u8 {
|
||||
return switch (self) {
|
||||
inline else => |tag| comptime w3c: {
|
||||
@setEvalBranchQuota(50_000);
|
||||
|
||||
const name = @tagName(tag);
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const w = fbs.writer();
|
||||
var i: usize = 0;
|
||||
while (i < name.len) {
|
||||
if (i == 0) {
|
||||
w.writeByte(std.ascii.toUpper(name[i])) catch unreachable;
|
||||
} else if (name[i] == '_') {
|
||||
i += 1;
|
||||
w.writeByte(std.ascii.toUpper(name[i])) catch unreachable;
|
||||
} else {
|
||||
w.writeByte(name[i]) catch unreachable;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const written = buf;
|
||||
const result = written[0..fbs.getWritten().len];
|
||||
break :w3c result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// True if this key represents a printable character.
|
||||
pub fn printable(self: Key) bool {
|
||||
return switch (self) {
|
||||
@@ -781,6 +849,18 @@ pub const Key = enum(c_int) {
|
||||
try testing.expect(!Key.digit_1.keypad());
|
||||
}
|
||||
|
||||
test "w3c" {
|
||||
// All our keys should convert to and from the W3C format.
|
||||
// We don't support every key in the W3C spec, so we only
|
||||
// check the enum fields.
|
||||
const testing = std.testing;
|
||||
inline for (@typeInfo(Key).@"enum".fields) |field| {
|
||||
const key = @field(Key, field.name);
|
||||
const w3c_name = key.w3c();
|
||||
try testing.expectEqual(key, Key.fromW3C(w3c_name).?);
|
||||
}
|
||||
}
|
||||
|
||||
const codepoint_map: []const struct { u21, Key } = &.{
|
||||
.{ 'a', .key_a },
|
||||
.{ 'b', .key_b },
|
||||
|
Reference in New Issue
Block a user