mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
make keyboard modifiers left/right-aware throughout core
This commit is contained in:
@ -72,12 +72,16 @@ typedef int ghostty_input_scroll_mods_t;
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_MODS_NONE = 0,
|
GHOSTTY_MODS_NONE = 0,
|
||||||
GHOSTTY_MODS_SHIFT = 1 << 0,
|
GHOSTTY_MODS_LEFT_SHIFT = 1 << 0,
|
||||||
GHOSTTY_MODS_CTRL = 1 << 1,
|
GHOSTTY_MODS_RIGHT_SHIFT = 1 << 1,
|
||||||
GHOSTTY_MODS_ALT = 1 << 2,
|
GHOSTTY_MODS_LEFT_CTRL = 1 << 2,
|
||||||
GHOSTTY_MODS_SUPER = 1 << 3,
|
GHOSTTY_MODS_RIGHT_CTRL = 1 << 3,
|
||||||
GHOSTTY_MODS_CAPS = 1 << 4,
|
GHOSTTY_MODS_LEFT_ALT = 1 << 4,
|
||||||
GHOSTTY_MODS_NUM = 1 << 5,
|
GHOSTTY_MODS_RIGHT_ALT = 1 << 5,
|
||||||
|
GHOSTTY_MODS_LEFT_SUPER = 1 << 6,
|
||||||
|
GHOSTTY_MODS_RIGHT_SUPER = 1 << 7,
|
||||||
|
GHOSTTY_MODS_CAPS = 1 << 8,
|
||||||
|
GHOSTTY_MODS_NUM = 1 << 9,
|
||||||
} ghostty_input_mods_e;
|
} ghostty_input_mods_e;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -491,10 +491,16 @@ extension Ghostty {
|
|||||||
|
|
||||||
private static func translateFlags(_ flags: NSEvent.ModifierFlags) -> ghostty_input_mods_e {
|
private static func translateFlags(_ flags: NSEvent.ModifierFlags) -> ghostty_input_mods_e {
|
||||||
var mods: UInt32 = GHOSTTY_MODS_NONE.rawValue
|
var mods: UInt32 = GHOSTTY_MODS_NONE.rawValue
|
||||||
if (flags.contains(.shift)) { mods |= GHOSTTY_MODS_SHIFT.rawValue }
|
|
||||||
if (flags.contains(.control)) { mods |= GHOSTTY_MODS_CTRL.rawValue }
|
let rawFlags = flags.rawValue
|
||||||
if (flags.contains(.option)) { mods |= GHOSTTY_MODS_ALT.rawValue }
|
if (rawFlags & UInt(NX_DEVICELSHIFTKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_SHIFT.rawValue }
|
||||||
if (flags.contains(.command)) { mods |= GHOSTTY_MODS_SUPER.rawValue }
|
if (rawFlags & UInt(NX_DEVICERSHIFTKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_SHIFT.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICELCTLKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_CTRL.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICERCTLKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_CTRL.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICELALTKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_ALT.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICERALTKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_ALT.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICELCMDKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_SUPER.rawValue }
|
||||||
|
if (rawFlags & UInt(NX_DEVICERCMDKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_SUPER.rawValue }
|
||||||
if (flags.contains(.capsLock)) { mods |= GHOSTTY_MODS_CAPS.rawValue }
|
if (flags.contains(.capsLock)) { mods |= GHOSTTY_MODS_CAPS.rawValue }
|
||||||
|
|
||||||
return ghostty_input_mods_e(mods)
|
return ghostty_input_mods_e(mods)
|
||||||
|
@ -981,12 +981,12 @@ pub fn keyCallback(
|
|||||||
|
|
||||||
// Handle non-printables
|
// Handle non-printables
|
||||||
const char: u8 = char: {
|
const char: u8 = char: {
|
||||||
const mods_int: u8 = @bitCast(mods);
|
const mods_int: input.Mods.Int = @bitCast(mods);
|
||||||
const ctrl_only: u8 = @bitCast(input.Mods{ .ctrl = true });
|
const ctrl_only: input.Mods.Int = @bitCast(input.Mods{ .ctrl = .both });
|
||||||
|
|
||||||
// If we're only pressing control, check if this is a character
|
// If we're only pressing control, check if this is a character
|
||||||
// we convert to a non-printable.
|
// we convert to a non-printable.
|
||||||
if (mods_int == ctrl_only) {
|
if (mods_int & ctrl_only > 0) {
|
||||||
const val: u8 = switch (key) {
|
const val: u8 = switch (key) {
|
||||||
.left_bracket => 0x1B,
|
.left_bracket => 0x1B,
|
||||||
.backslash => 0x1C,
|
.backslash => 0x1C,
|
||||||
@ -1324,9 +1324,9 @@ fn mouseReport(
|
|||||||
|
|
||||||
// X10 doesn't have modifiers
|
// X10 doesn't have modifiers
|
||||||
if (self.io.terminal.modes.mouse_event != .x10) {
|
if (self.io.terminal.modes.mouse_event != .x10) {
|
||||||
if (mods.shift) acc += 4;
|
if (mods.shift.pressed()) acc += 4;
|
||||||
if (mods.super) acc += 8;
|
if (mods.super.pressed()) acc += 8;
|
||||||
if (mods.ctrl) acc += 16;
|
if (mods.ctrl.pressed()) acc += 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Motion adds another bit
|
// Motion adds another bit
|
||||||
@ -1478,13 +1478,13 @@ pub fn mouseButtonCallback(
|
|||||||
|
|
||||||
// Always record our latest mouse state
|
// Always record our latest mouse state
|
||||||
self.mouse.click_state[@intCast(@intFromEnum(button))] = action;
|
self.mouse.click_state[@intCast(@intFromEnum(button))] = action;
|
||||||
self.mouse.mods = @bitCast(mods);
|
self.mouse.mods = mods;
|
||||||
|
|
||||||
// Shift-click continues the previous mouse state if we have a selection.
|
// Shift-click continues the previous mouse state if we have a selection.
|
||||||
// cursorPosCallback will also do a mouse report so we don't need to do any
|
// cursorPosCallback will also do a mouse report so we don't need to do any
|
||||||
// of the logic below.
|
// of the logic below.
|
||||||
if (button == .left and action == .press) {
|
if (button == .left and action == .press) {
|
||||||
if (mods.shift and self.mouse.left_click_count > 0) {
|
if (mods.shift.pressed() and self.mouse.left_click_count > 0) {
|
||||||
// Checking for selection requires the renderer state mutex which
|
// Checking for selection requires the renderer state mutex which
|
||||||
// sucks but this should be pretty rare of an event so it won't
|
// sucks but this should be pretty rare of an event so it won't
|
||||||
// cause a ton of contention.
|
// cause a ton of contention.
|
||||||
@ -1508,7 +1508,7 @@ pub fn mouseButtonCallback(
|
|||||||
// Report mouse events if enabled
|
// Report mouse events if enabled
|
||||||
if (self.io.terminal.modes.mouse_event != .none) report: {
|
if (self.io.terminal.modes.mouse_event != .none) report: {
|
||||||
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
||||||
if (mods.shift) break :report;
|
if (mods.shift.pressed()) break :report;
|
||||||
|
|
||||||
// In any other mouse button scenario without shift pressed we
|
// In any other mouse button scenario without shift pressed we
|
||||||
// clear the selection since the underlying application can handle
|
// clear the selection since the underlying application can handle
|
||||||
@ -1634,7 +1634,7 @@ pub fn cursorPosCallback(
|
|||||||
// Do a mouse report
|
// Do a mouse report
|
||||||
if (self.io.terminal.modes.mouse_event != .none) report: {
|
if (self.io.terminal.modes.mouse_event != .none) report: {
|
||||||
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
||||||
if (self.mouse.mods.shift) break :report;
|
if (self.mouse.mods.shift.pressed()) break :report;
|
||||||
|
|
||||||
// We use the first mouse button we find pressed in order to report
|
// We use the first mouse button we find pressed in order to report
|
||||||
// since the spec (afaict) does not say...
|
// since the spec (afaict) does not say...
|
||||||
|
@ -508,7 +508,7 @@ pub const CAPI = struct {
|
|||||||
action,
|
action,
|
||||||
key,
|
key,
|
||||||
unmapped_key,
|
unmapped_key,
|
||||||
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(mods))))),
|
@bitCast(@as(input.Mods.Int, @truncate(@as(c_uint, @bitCast(mods))))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +527,7 @@ pub const CAPI = struct {
|
|||||||
surface.mouseButtonCallback(
|
surface.mouseButtonCallback(
|
||||||
action,
|
action,
|
||||||
button,
|
button,
|
||||||
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(mods))))),
|
@bitCast(@as(input.Mods.Int, @truncate(@as(c_uint, @bitCast(mods))))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +545,7 @@ pub const CAPI = struct {
|
|||||||
surface.scrollCallback(
|
surface.scrollCallback(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
|
@bitCast(@as(input.ScrollMods.Int, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,7 +564,7 @@ pub const Surface = struct {
|
|||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
// Convert our glfw types into our input types
|
// Convert our glfw types into our input types
|
||||||
const mods: input.Mods = @bitCast(glfw_mods);
|
const mods = convertMods(glfw_mods);
|
||||||
const action: input.Action = switch (glfw_action) {
|
const action: input.Action = switch (glfw_action) {
|
||||||
.release => .release,
|
.release => .release,
|
||||||
.press => .press,
|
.press => .press,
|
||||||
@ -784,7 +784,7 @@ pub const Surface = struct {
|
|||||||
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
|
|
||||||
// Convert glfw button to input button
|
// Convert glfw button to input button
|
||||||
const mods: input.Mods = @bitCast(glfw_mods);
|
const mods = convertMods(glfw_mods);
|
||||||
const button: input.MouseButton = switch (glfw_button) {
|
const button: input.MouseButton = switch (glfw_button) {
|
||||||
.left => .left,
|
.left => .left,
|
||||||
.right => .right,
|
.right => .right,
|
||||||
@ -806,4 +806,15 @@ pub const Surface = struct {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convertMods(mods: glfw.Mods) input.Mods {
|
||||||
|
return .{
|
||||||
|
.shift = if (mods.shift) .both else .none,
|
||||||
|
.ctrl = if (mods.control) .both else .none,
|
||||||
|
.alt = if (mods.alt) .both else .none,
|
||||||
|
.super = if (mods.super) .both else .none,
|
||||||
|
.caps_lock = mods.caps_lock,
|
||||||
|
.num_lock = mods.num_lock,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -300,7 +300,7 @@ pub const Config = struct {
|
|||||||
// Add our default keybindings
|
// Add our default keybindings
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .space, .mods = .{ .super = true, .alt = true, .ctrl = true } },
|
.{ .key = .space, .mods = .{ .super = .both, .alt = .both, .ctrl = .both } },
|
||||||
.{ .reload_config = {} },
|
.{ .reload_config = {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -308,9 +308,9 @@ pub const Config = struct {
|
|||||||
// On macOS we default to super but Linux ctrl+shift since
|
// On macOS we default to super but Linux ctrl+shift since
|
||||||
// ctrl+c is to kill the process.
|
// ctrl+c is to kill the process.
|
||||||
const mods: inputpkg.Mods = if (builtin.target.isDarwin())
|
const mods: inputpkg.Mods = if (builtin.target.isDarwin())
|
||||||
.{ .super = true }
|
.{ .super = .both }
|
||||||
else
|
else
|
||||||
.{ .ctrl = true, .shift = true };
|
.{ .ctrl = .both, .shift = .both };
|
||||||
|
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
@ -393,13 +393,13 @@ pub const Config = struct {
|
|||||||
// Dev Mode
|
// Dev Mode
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .down, .mods = .{ .shift = true, .super = true } },
|
.{ .key = .down, .mods = .{ .shift = .both, .super = .both } },
|
||||||
.{ .toggle_dev_mode = {} },
|
.{ .toggle_dev_mode = {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .j, .mods = ctrlOrSuper(.{ .shift = true }) },
|
.{ .key = .j, .mods = ctrlOrSuper(.{ .shift = .both }) },
|
||||||
.{ .write_scrollback_file = {} },
|
.{ .write_scrollback_file = {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -407,89 +407,89 @@ pub const Config = struct {
|
|||||||
if (comptime !builtin.target.isDarwin()) {
|
if (comptime !builtin.target.isDarwin()) {
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .n, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .n, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .new_window = {} },
|
.{ .new_window = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .w, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .w, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .close_surface = {} },
|
.{ .close_surface = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .q, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .q, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .quit = {} },
|
.{ .quit = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .f4, .mods = .{ .alt = true } },
|
.{ .key = .f4, .mods = .{ .alt = .both } },
|
||||||
.{ .close_window = {} },
|
.{ .close_window = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .t, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .t, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .new_tab = {} },
|
.{ .new_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .left, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .previous_tab = {} },
|
.{ .previous_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .right, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .next_tab = {} },
|
.{ .next_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .o, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .o, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .new_split = .right },
|
.{ .new_split = .right },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .e, .mods = .{ .ctrl = true, .shift = true } },
|
.{ .key = .e, .mods = .{ .ctrl = .both, .shift = .both } },
|
||||||
.{ .new_split = .down },
|
.{ .new_split = .down },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left_bracket, .mods = .{ .ctrl = true, .super = true } },
|
.{ .key = .left_bracket, .mods = .{ .ctrl = .both, .super = .both } },
|
||||||
.{ .goto_split = .previous },
|
.{ .goto_split = .previous },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right_bracket, .mods = .{ .ctrl = true, .super = true } },
|
.{ .key = .right_bracket, .mods = .{ .ctrl = .both, .super = .both } },
|
||||||
.{ .goto_split = .next },
|
.{ .goto_split = .next },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .up, .mods = .{ .ctrl = true, .alt = true } },
|
.{ .key = .up, .mods = .{ .ctrl = .both, .alt = .both } },
|
||||||
.{ .goto_split = .top },
|
.{ .goto_split = .top },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .down, .mods = .{ .ctrl = true, .alt = true } },
|
.{ .key = .down, .mods = .{ .ctrl = .both, .alt = .both } },
|
||||||
.{ .goto_split = .bottom },
|
.{ .goto_split = .bottom },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left, .mods = .{ .ctrl = true, .alt = true } },
|
.{ .key = .left, .mods = .{ .ctrl = .both, .alt = .both } },
|
||||||
.{ .goto_split = .left },
|
.{ .goto_split = .left },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right, .mods = .{ .ctrl = true, .alt = true } },
|
.{ .key = .right, .mods = .{ .ctrl = .both, .alt = .both } },
|
||||||
.{ .goto_split = .right },
|
.{ .goto_split = .right },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Semantic prompts
|
// Semantic prompts
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .page_up, .mods = .{ .shift = true } },
|
.{ .key = .page_up, .mods = .{ .shift = .both } },
|
||||||
.{ .jump_to_prompt = -1 },
|
.{ .jump_to_prompt = -1 },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .page_down, .mods = .{ .shift = true } },
|
.{ .key = .page_down, .mods = .{ .shift = .both } },
|
||||||
.{ .jump_to_prompt = 1 },
|
.{ .jump_to_prompt = 1 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -502,9 +502,9 @@ pub const Config = struct {
|
|||||||
// On macOS we default to super but everywhere else
|
// On macOS we default to super but everywhere else
|
||||||
// is alt.
|
// is alt.
|
||||||
const mods: inputpkg.Mods = if (builtin.target.isDarwin())
|
const mods: inputpkg.Mods = if (builtin.target.isDarwin())
|
||||||
.{ .super = true }
|
.{ .super = .both }
|
||||||
else
|
else
|
||||||
.{ .alt = true };
|
.{ .alt = .both };
|
||||||
|
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
@ -525,97 +525,97 @@ pub const Config = struct {
|
|||||||
if (comptime builtin.target.isDarwin()) {
|
if (comptime builtin.target.isDarwin()) {
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .q, .mods = .{ .super = true } },
|
.{ .key = .q, .mods = .{ .super = .both } },
|
||||||
.{ .quit = {} },
|
.{ .quit = {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .k, .mods = .{ .super = true } },
|
.{ .key = .k, .mods = .{ .super = .both } },
|
||||||
.{ .clear_screen = {} },
|
.{ .clear_screen = {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Semantic prompts
|
// Semantic prompts
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .up, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .up, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .jump_to_prompt = -1 },
|
.{ .jump_to_prompt = -1 },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .down, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .down, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .jump_to_prompt = 1 },
|
.{ .jump_to_prompt = 1 },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mac windowing
|
// Mac windowing
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .n, .mods = .{ .super = true } },
|
.{ .key = .n, .mods = .{ .super = .both } },
|
||||||
.{ .new_window = {} },
|
.{ .new_window = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .w, .mods = .{ .super = true } },
|
.{ .key = .w, .mods = .{ .super = .both } },
|
||||||
.{ .close_surface = {} },
|
.{ .close_surface = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .w, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .w, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .close_window = {} },
|
.{ .close_window = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .t, .mods = .{ .super = true } },
|
.{ .key = .t, .mods = .{ .super = .both } },
|
||||||
.{ .new_tab = {} },
|
.{ .new_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left_bracket, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .left_bracket, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .previous_tab = {} },
|
.{ .previous_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right_bracket, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .right_bracket, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .next_tab = {} },
|
.{ .next_tab = {} },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .d, .mods = .{ .super = true } },
|
.{ .key = .d, .mods = .{ .super = .both } },
|
||||||
.{ .new_split = .right },
|
.{ .new_split = .right },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .d, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .d, .mods = .{ .super = .both, .shift = .both } },
|
||||||
.{ .new_split = .down },
|
.{ .new_split = .down },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left_bracket, .mods = .{ .super = true } },
|
.{ .key = .left_bracket, .mods = .{ .super = .both } },
|
||||||
.{ .goto_split = .previous },
|
.{ .goto_split = .previous },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right_bracket, .mods = .{ .super = true } },
|
.{ .key = .right_bracket, .mods = .{ .super = .both } },
|
||||||
.{ .goto_split = .next },
|
.{ .goto_split = .next },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .up, .mods = .{ .super = true, .alt = true } },
|
.{ .key = .up, .mods = .{ .super = .both, .alt = .both } },
|
||||||
.{ .goto_split = .top },
|
.{ .goto_split = .top },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .down, .mods = .{ .super = true, .alt = true } },
|
.{ .key = .down, .mods = .{ .super = .both, .alt = .both } },
|
||||||
.{ .goto_split = .bottom },
|
.{ .goto_split = .bottom },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .left, .mods = .{ .super = true, .alt = true } },
|
.{ .key = .left, .mods = .{ .super = .both, .alt = .both } },
|
||||||
.{ .goto_split = .left },
|
.{ .goto_split = .left },
|
||||||
);
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .right, .mods = .{ .super = true, .alt = true } },
|
.{ .key = .right, .mods = .{ .super = .both, .alt = .both } },
|
||||||
.{ .goto_split = .right },
|
.{ .goto_split = .right },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -630,9 +630,9 @@ pub const Config = struct {
|
|||||||
fn ctrlOrSuper(mods: inputpkg.Mods) inputpkg.Mods {
|
fn ctrlOrSuper(mods: inputpkg.Mods) inputpkg.Mods {
|
||||||
var copy = mods;
|
var copy = mods;
|
||||||
if (comptime builtin.target.isDarwin()) {
|
if (comptime builtin.target.isDarwin()) {
|
||||||
copy.super = true;
|
copy.super = .both;
|
||||||
} else {
|
} else {
|
||||||
copy.ctrl = true;
|
copy.ctrl = .both;
|
||||||
}
|
}
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
|
@ -42,12 +42,24 @@ pub fn parse(input: []const u8) !Binding {
|
|||||||
// Check if its a modifier
|
// Check if its a modifier
|
||||||
const modsInfo = @typeInfo(key.Mods).Struct;
|
const modsInfo = @typeInfo(key.Mods).Struct;
|
||||||
inline for (modsInfo.fields) |field| {
|
inline for (modsInfo.fields) |field| {
|
||||||
if (field.type == bool) {
|
if (field.name[0] != '_') {
|
||||||
if (std.mem.eql(u8, part, field.name)) {
|
if (std.mem.eql(u8, part, field.name)) {
|
||||||
// Repeat not allowed
|
switch (field.type) {
|
||||||
if (@field(result.mods, field.name)) return Error.InvalidFormat;
|
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;
|
continue :loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,14 +348,14 @@ test "parse: triggers" {
|
|||||||
// single modifier
|
// single modifier
|
||||||
try testing.expectEqual(Binding{
|
try testing.expectEqual(Binding{
|
||||||
.trigger = .{
|
.trigger = .{
|
||||||
.mods = .{ .shift = true },
|
.mods = .{ .shift = .both },
|
||||||
.key = .a,
|
.key = .a,
|
||||||
},
|
},
|
||||||
.action = .{ .ignore = {} },
|
.action = .{ .ignore = {} },
|
||||||
}, try parse("shift+a=ignore"));
|
}, try parse("shift+a=ignore"));
|
||||||
try testing.expectEqual(Binding{
|
try testing.expectEqual(Binding{
|
||||||
.trigger = .{
|
.trigger = .{
|
||||||
.mods = .{ .ctrl = true },
|
.mods = .{ .ctrl = .both },
|
||||||
.key = .a,
|
.key = .a,
|
||||||
},
|
},
|
||||||
.action = .{ .ignore = {} },
|
.action = .{ .ignore = {} },
|
||||||
@ -352,7 +364,7 @@ test "parse: triggers" {
|
|||||||
// multiple modifier
|
// multiple modifier
|
||||||
try testing.expectEqual(Binding{
|
try testing.expectEqual(Binding{
|
||||||
.trigger = .{
|
.trigger = .{
|
||||||
.mods = .{ .shift = true, .ctrl = true },
|
.mods = .{ .shift = .both, .ctrl = .both },
|
||||||
.key = .a,
|
.key = .a,
|
||||||
},
|
},
|
||||||
.action = .{ .ignore = {} },
|
.action = .{ .ignore = {} },
|
||||||
@ -361,7 +373,7 @@ test "parse: triggers" {
|
|||||||
// key can come before modifier
|
// key can come before modifier
|
||||||
try testing.expectEqual(Binding{
|
try testing.expectEqual(Binding{
|
||||||
.trigger = .{
|
.trigger = .{
|
||||||
.mods = .{ .shift = true },
|
.mods = .{ .shift = .both },
|
||||||
.key = .a,
|
.key = .a,
|
||||||
},
|
},
|
||||||
.action = .{ .ignore = {} },
|
.action = .{ .ignore = {} },
|
||||||
@ -370,7 +382,7 @@ test "parse: triggers" {
|
|||||||
// unmapped keys
|
// unmapped keys
|
||||||
try testing.expectEqual(Binding{
|
try testing.expectEqual(Binding{
|
||||||
.trigger = .{
|
.trigger = .{
|
||||||
.mods = .{ .shift = true },
|
.mods = .{ .shift = .both },
|
||||||
.key = .a,
|
.key = .a,
|
||||||
.unmapped = true,
|
.unmapped = true,
|
||||||
},
|
},
|
||||||
|
@ -5,22 +5,41 @@ const Allocator = std.mem.Allocator;
|
|||||||
/// GLFW representation, but we use this generically.
|
/// GLFW representation, but we use this generically.
|
||||||
///
|
///
|
||||||
/// IMPORTANT: Any changes here update include/ghostty.h
|
/// IMPORTANT: Any changes here update include/ghostty.h
|
||||||
pub const Mods = packed struct(u8) {
|
pub const Mods = packed struct(Mods.Int) {
|
||||||
shift: bool = false,
|
pub const Int = u10;
|
||||||
ctrl: bool = false,
|
|
||||||
alt: bool = false,
|
shift: Side = .none,
|
||||||
super: bool = false,
|
ctrl: Side = .none,
|
||||||
|
alt: Side = .none,
|
||||||
|
super: Side = .none,
|
||||||
caps_lock: bool = false,
|
caps_lock: bool = false,
|
||||||
num_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
|
// For our own understanding
|
||||||
test {
|
test {
|
||||||
const testing = std.testing;
|
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(
|
try testing.expectEqual(
|
||||||
@as(u8, @bitCast(Mods{ .shift = true })),
|
@as(Int, @bitCast(Mods{ .shift = .left })),
|
||||||
@as(u8, 0b0000_0001),
|
@as(Int, 0b0000_0001),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,9 @@ pub const MouseMomentum = enum(u3) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// The bitmask for mods for scroll events.
|
/// 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
|
/// True if this is a high-precision scroll event. For example, Apple
|
||||||
/// devices such as Magic Mouse, trackpads, etc. are high-precision
|
/// devices such as Magic Mouse, trackpads, etc. are high-precision
|
||||||
/// and send very detailed scroll events.
|
/// and send very detailed scroll events.
|
||||||
@ -79,10 +81,10 @@ pub const ScrollMods = packed struct(u8) {
|
|||||||
// For our own understanding
|
// For our own understanding
|
||||||
test {
|
test {
|
||||||
const testing = std.testing;
|
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(
|
try testing.expectEqual(
|
||||||
@as(u8, @bitCast(ScrollMods{ .precision = true })),
|
@as(Int, @bitCast(ScrollMods{ .precision = true })),
|
||||||
@as(u8, 0b0000_0001),
|
@as(Int, 0b0000_0001),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user