mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-17 19:24:52 +00:00
make keyboard modifiers left/right-aware throughout core
This commit is contained in:
@@ -42,12 +42,24 @@ pub fn parse(input: []const u8) !Binding {
|
||||
// Check if its a modifier
|
||||
const modsInfo = @typeInfo(key.Mods).Struct;
|
||||
inline for (modsInfo.fields) |field| {
|
||||
if (field.type == bool) {
|
||||
if (field.name[0] != '_') {
|
||||
if (std.mem.eql(u8, part, field.name)) {
|
||||
// Repeat not allowed
|
||||
if (@field(result.mods, field.name)) return Error.InvalidFormat;
|
||||
switch (field.type) {
|
||||
bool => {
|
||||
if (@field(result.mods, field.name))
|
||||
return Error.InvalidFormat;
|
||||
@field(result.mods, field.name) = true;
|
||||
},
|
||||
|
||||
key.Mods.Side => {
|
||||
if (@field(result.mods, field.name).pressed())
|
||||
return Error.InvalidFormat;
|
||||
@field(result.mods, field.name) = .both;
|
||||
},
|
||||
|
||||
else => @compileError("invalid type"),
|
||||
}
|
||||
|
||||
@field(result.mods, field.name) = true;
|
||||
continue :loop;
|
||||
}
|
||||
}
|
||||
@@ -336,14 +348,14 @@ test "parse: triggers" {
|
||||
// single modifier
|
||||
try testing.expectEqual(Binding{
|
||||
.trigger = .{
|
||||
.mods = .{ .shift = true },
|
||||
.mods = .{ .shift = .both },
|
||||
.key = .a,
|
||||
},
|
||||
.action = .{ .ignore = {} },
|
||||
}, try parse("shift+a=ignore"));
|
||||
try testing.expectEqual(Binding{
|
||||
.trigger = .{
|
||||
.mods = .{ .ctrl = true },
|
||||
.mods = .{ .ctrl = .both },
|
||||
.key = .a,
|
||||
},
|
||||
.action = .{ .ignore = {} },
|
||||
@@ -352,7 +364,7 @@ test "parse: triggers" {
|
||||
// multiple modifier
|
||||
try testing.expectEqual(Binding{
|
||||
.trigger = .{
|
||||
.mods = .{ .shift = true, .ctrl = true },
|
||||
.mods = .{ .shift = .both, .ctrl = .both },
|
||||
.key = .a,
|
||||
},
|
||||
.action = .{ .ignore = {} },
|
||||
@@ -361,7 +373,7 @@ test "parse: triggers" {
|
||||
// key can come before modifier
|
||||
try testing.expectEqual(Binding{
|
||||
.trigger = .{
|
||||
.mods = .{ .shift = true },
|
||||
.mods = .{ .shift = .both },
|
||||
.key = .a,
|
||||
},
|
||||
.action = .{ .ignore = {} },
|
||||
@@ -370,7 +382,7 @@ test "parse: triggers" {
|
||||
// unmapped keys
|
||||
try testing.expectEqual(Binding{
|
||||
.trigger = .{
|
||||
.mods = .{ .shift = true },
|
||||
.mods = .{ .shift = .both },
|
||||
.key = .a,
|
||||
.unmapped = true,
|
||||
},
|
||||
|
||||
@@ -5,22 +5,41 @@ const Allocator = std.mem.Allocator;
|
||||
/// GLFW representation, but we use this generically.
|
||||
///
|
||||
/// IMPORTANT: Any changes here update include/ghostty.h
|
||||
pub const Mods = packed struct(u8) {
|
||||
shift: bool = false,
|
||||
ctrl: bool = false,
|
||||
alt: bool = false,
|
||||
super: bool = false,
|
||||
pub const Mods = packed struct(Mods.Int) {
|
||||
pub const Int = u10;
|
||||
|
||||
shift: Side = .none,
|
||||
ctrl: Side = .none,
|
||||
alt: Side = .none,
|
||||
super: Side = .none,
|
||||
caps_lock: bool = false,
|
||||
num_lock: bool = false,
|
||||
_padding: u2 = 0,
|
||||
|
||||
/// Keeps track of left/right press. A packed struct makes it easy
|
||||
/// to set as a bitmask and then check the individual values.
|
||||
pub const Side = enum(u2) {
|
||||
none = 0,
|
||||
left = 1,
|
||||
right = 2,
|
||||
|
||||
/// Note that while this should only be set for BOTH being set,
|
||||
/// this is semantically used to mean "any" for the purposes of
|
||||
/// keybindings. We do not allow keybindings to map to "both".
|
||||
both = 3,
|
||||
|
||||
/// Returns true if the key is pressed at all.
|
||||
pub fn pressed(self: Side) bool {
|
||||
return @intFromEnum(self) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
// For our own understanding
|
||||
test {
|
||||
const testing = std.testing;
|
||||
try testing.expectEqual(@as(u8, @bitCast(Mods{})), @as(u8, 0b0));
|
||||
try testing.expectEqual(@as(Int, @bitCast(Mods{})), @as(Int, 0b0));
|
||||
try testing.expectEqual(
|
||||
@as(u8, @bitCast(Mods{ .shift = true })),
|
||||
@as(u8, 0b0000_0001),
|
||||
@as(Int, @bitCast(Mods{ .shift = .left })),
|
||||
@as(Int, 0b0000_0001),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,7 +64,9 @@ pub const MouseMomentum = enum(u3) {
|
||||
};
|
||||
|
||||
/// The bitmask for mods for scroll events.
|
||||
pub const ScrollMods = packed struct(u8) {
|
||||
pub const ScrollMods = packed struct(ScrollMods.Int) {
|
||||
pub const Int = u8;
|
||||
|
||||
/// True if this is a high-precision scroll event. For example, Apple
|
||||
/// devices such as Magic Mouse, trackpads, etc. are high-precision
|
||||
/// and send very detailed scroll events.
|
||||
@@ -79,10 +81,10 @@ pub const ScrollMods = packed struct(u8) {
|
||||
// For our own understanding
|
||||
test {
|
||||
const testing = std.testing;
|
||||
try testing.expectEqual(@as(u8, @bitCast(ScrollMods{})), @as(u8, 0b0));
|
||||
try testing.expectEqual(@as(Int, @bitCast(ScrollMods{})), @as(Int, 0b0));
|
||||
try testing.expectEqual(
|
||||
@as(u8, @bitCast(ScrollMods{ .precision = true })),
|
||||
@as(u8, 0b0000_0001),
|
||||
@as(Int, @bitCast(ScrollMods{ .precision = true })),
|
||||
@as(Int, 0b0000_0001),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user