diff --git a/src/Surface.zig b/src/Surface.zig index e1b226da1..80d18f47e 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1149,8 +1149,8 @@ pub fn keyCallback( .set_other => if (!modify_other_keys) continue, } - const mods_int: u8 = @bitCast(binding_mods); - const entry_mods_int: u8 = @bitCast(entry.mods); + const mods_int = binding_mods.int(); + const entry_mods_int = entry.mods.int(); if (entry_mods_int == 0) { if (mods_int != 0 and !entry.mods_empty_is_any) continue; // mods are either empty, or empty means any so we allow it. @@ -1186,8 +1186,8 @@ pub fn keyCallback( // Handle non-printables const char: u8 = char: { - const mods_int: u8 = @bitCast(unalt_mods); - const ctrl_only: u8 = @bitCast(input.Mods{ .ctrl = true }); + const mods_int = unalt_mods.int(); + const ctrl_only = (input.Mods{ .ctrl = true }).int(); // If we're only pressing control, check if this is a character // we convert to a non-printable. The best table I've found for diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index efd1480af..7c2f0514a 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -660,7 +660,10 @@ pub const CAPI = struct { surface.keyCallback( action, keycode, - @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(c_mods))))), + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(c_mods))), + )), ) catch |err| { log.err("error processing key event err={}", .{err}); return; @@ -684,7 +687,10 @@ pub const CAPI = struct { surface.mouseButtonCallback( action, button, - @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(mods))))), + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods))), + )), ); } diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 90d08dfba..bb00efafe 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -618,7 +618,12 @@ pub const Surface = struct { const core_win = window.getUserPointer(CoreSurface) orelse return; // Convert our glfw types into our input types - const mods: input.Mods = @bitCast(glfw_mods); + const mods: input.Mods = .{ + .shift = glfw_mods.shift, + .ctrl = glfw_mods.control, + .alt = glfw_mods.alt, + .super = glfw_mods.super, + }; const action: input.Action = switch (glfw_action) { .release => .release, .press => .press, @@ -843,7 +848,12 @@ pub const Surface = struct { const core_win = window.getUserPointer(CoreSurface) orelse return; // Convert glfw button to input button - const mods: input.Mods = @bitCast(glfw_mods); + const mods: input.Mods = .{ + .shift = glfw_mods.shift, + .ctrl = glfw_mods.control, + .alt = glfw_mods.alt, + .super = glfw_mods.super, + }; const button: input.MouseButton = switch (glfw_button) { .left => .left, .right => .right, diff --git a/src/input/key.zig b/src/input/key.zig index 99ebeb192..8ec485c9a 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -5,23 +5,45 @@ 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) { +pub const Mods = packed struct(Mods.Backing) { + pub const Backing = u16; + shift: bool = false, ctrl: bool = false, alt: bool = false, super: bool = false, caps_lock: bool = false, num_lock: bool = false, - _padding: u2 = 0, + sides: side = .{}, + _padding: u6 = 0, + + /// Tracks the side that is active for any given modifier. Note + /// that this doesn't confirm a modifier is pressed; you must check + /// the bool for that in addition to this. + /// + /// Not all platforms support this, check apprt for more info. + pub const side = packed struct(u4) { + shift: Side = .left, + ctrl: Side = .left, + alt: Side = .left, + super: Side = .left, + }; + + pub const Side = enum { left, right }; + + /// Integer value of this struct. + pub fn int(self: Mods) Backing { + return @bitCast(self); + } /// Returns true if no modifiers are set. pub fn empty(self: Mods) bool { - return @as(u8, @bitCast(self)) == 0; + return self.int() == 0; } /// Returns true if two mods are equal. pub fn equal(self: Mods, other: Mods) bool { - return @as(u8, @bitCast(self)) == @as(u8, @bitCast(other)); + return self.int() == other.int(); } /// Return mods that are only relevant for bindings. @@ -45,10 +67,10 @@ pub const Mods = packed struct(u8) { // For our own understanding test { const testing = std.testing; - try testing.expectEqual(@as(u8, @bitCast(Mods{})), @as(u8, 0b0)); + try testing.expectEqual(@as(Backing, @bitCast(Mods{})), @as(Backing, 0b0)); try testing.expectEqual( - @as(u8, @bitCast(Mods{ .shift = true })), - @as(u8, 0b0000_0001), + @as(Backing, @bitCast(Mods{ .shift = true })), + @as(Backing, 0b0000_0001), ); } };