Skip to content

Commit c139279

Browse files
authored
Merge pull request #244 from mitchellh/alt-as-esc
Make Ghostty aware of left/right modifier keys
2 parents 4ed2104 + 274f934 commit c139279

File tree

10 files changed

+210
-98
lines changed

10 files changed

+210
-98
lines changed

include/ghostty.h

+10-6
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,16 @@ typedef int ghostty_input_scroll_mods_t;
7272

7373
typedef enum {
7474
GHOSTTY_MODS_NONE = 0,
75-
GHOSTTY_MODS_SHIFT = 1 << 0,
76-
GHOSTTY_MODS_CTRL = 1 << 1,
77-
GHOSTTY_MODS_ALT = 1 << 2,
78-
GHOSTTY_MODS_SUPER = 1 << 3,
79-
GHOSTTY_MODS_CAPS = 1 << 4,
80-
GHOSTTY_MODS_NUM = 1 << 5,
75+
GHOSTTY_MODS_LEFT_SHIFT = 1 << 0,
76+
GHOSTTY_MODS_RIGHT_SHIFT = 1 << 1,
77+
GHOSTTY_MODS_LEFT_CTRL = 1 << 2,
78+
GHOSTTY_MODS_RIGHT_CTRL = 1 << 3,
79+
GHOSTTY_MODS_LEFT_ALT = 1 << 4,
80+
GHOSTTY_MODS_RIGHT_ALT = 1 << 5,
81+
GHOSTTY_MODS_LEFT_SUPER = 1 << 6,
82+
GHOSTTY_MODS_RIGHT_SUPER = 1 << 7,
83+
GHOSTTY_MODS_CAPS = 1 << 8,
84+
GHOSTTY_MODS_NUM = 1 << 9,
8185
} ghostty_input_mods_e;
8286

8387
typedef enum {

macos/Sources/Ghostty/SurfaceView.swift

+10-4
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,16 @@ extension Ghostty {
491491

492492
private static func translateFlags(_ flags: NSEvent.ModifierFlags) -> ghostty_input_mods_e {
493493
var mods: UInt32 = GHOSTTY_MODS_NONE.rawValue
494-
if (flags.contains(.shift)) { mods |= GHOSTTY_MODS_SHIFT.rawValue }
495-
if (flags.contains(.control)) { mods |= GHOSTTY_MODS_CTRL.rawValue }
496-
if (flags.contains(.option)) { mods |= GHOSTTY_MODS_ALT.rawValue }
497-
if (flags.contains(.command)) { mods |= GHOSTTY_MODS_SUPER.rawValue }
494+
495+
let rawFlags = flags.rawValue
496+
if (rawFlags & UInt(NX_DEVICELSHIFTKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_SHIFT.rawValue }
497+
if (rawFlags & UInt(NX_DEVICERSHIFTKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_SHIFT.rawValue }
498+
if (rawFlags & UInt(NX_DEVICELCTLKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_CTRL.rawValue }
499+
if (rawFlags & UInt(NX_DEVICERCTLKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_CTRL.rawValue }
500+
if (rawFlags & UInt(NX_DEVICELALTKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_ALT.rawValue }
501+
if (rawFlags & UInt(NX_DEVICERALTKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_ALT.rawValue }
502+
if (rawFlags & UInt(NX_DEVICELCMDKEYMASK) != 0) { mods |= GHOSTTY_MODS_LEFT_SUPER.rawValue }
503+
if (rawFlags & UInt(NX_DEVICERCMDKEYMASK) != 0) { mods |= GHOSTTY_MODS_RIGHT_SUPER.rawValue }
498504
if (flags.contains(.capsLock)) { mods |= GHOSTTY_MODS_CAPS.rawValue }
499505

500506
return ghostty_input_mods_e(mods)

src/Surface.zig

+10-10
Original file line numberDiff line numberDiff line change
@@ -981,12 +981,12 @@ pub fn keyCallback(
981981

982982
// Handle non-printables
983983
const char: u8 = char: {
984-
const mods_int: u8 = @bitCast(mods);
985-
const ctrl_only: u8 = @bitCast(input.Mods{ .ctrl = true });
984+
const mods_int: input.Mods.Int = @bitCast(mods);
985+
const ctrl_only: input.Mods.Int = @bitCast(input.Mods{ .ctrl = .both });
986986

987987
// If we're only pressing control, check if this is a character
988988
// we convert to a non-printable.
989-
if (mods_int == ctrl_only) {
989+
if (mods_int & ctrl_only > 0) {
990990
const val: u8 = switch (key) {
991991
.left_bracket => 0x1B,
992992
.backslash => 0x1C,
@@ -1324,9 +1324,9 @@ fn mouseReport(
13241324

13251325
// X10 doesn't have modifiers
13261326
if (self.io.terminal.modes.mouse_event != .x10) {
1327-
if (mods.shift) acc += 4;
1328-
if (mods.super) acc += 8;
1329-
if (mods.ctrl) acc += 16;
1327+
if (mods.shift.pressed()) acc += 4;
1328+
if (mods.super.pressed()) acc += 8;
1329+
if (mods.ctrl.pressed()) acc += 16;
13301330
}
13311331

13321332
// Motion adds another bit
@@ -1478,13 +1478,13 @@ pub fn mouseButtonCallback(
14781478

14791479
// Always record our latest mouse state
14801480
self.mouse.click_state[@intCast(@intFromEnum(button))] = action;
1481-
self.mouse.mods = @bitCast(mods);
1481+
self.mouse.mods = mods;
14821482

14831483
// Shift-click continues the previous mouse state if we have a selection.
14841484
// cursorPosCallback will also do a mouse report so we don't need to do any
14851485
// of the logic below.
14861486
if (button == .left and action == .press) {
1487-
if (mods.shift and self.mouse.left_click_count > 0) {
1487+
if (mods.shift.pressed() and self.mouse.left_click_count > 0) {
14881488
// Checking for selection requires the renderer state mutex which
14891489
// sucks but this should be pretty rare of an event so it won't
14901490
// cause a ton of contention.
@@ -1508,7 +1508,7 @@ pub fn mouseButtonCallback(
15081508
// Report mouse events if enabled
15091509
if (self.io.terminal.modes.mouse_event != .none) report: {
15101510
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
1511-
if (mods.shift) break :report;
1511+
if (mods.shift.pressed()) break :report;
15121512

15131513
// In any other mouse button scenario without shift pressed we
15141514
// clear the selection since the underlying application can handle
@@ -1634,7 +1634,7 @@ pub fn cursorPosCallback(
16341634
// Do a mouse report
16351635
if (self.io.terminal.modes.mouse_event != .none) report: {
16361636
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
1637-
if (self.mouse.mods.shift) break :report;
1637+
if (self.mouse.mods.shift.pressed()) break :report;
16381638

16391639
// We use the first mouse button we find pressed in order to report
16401640
// since the spec (afaict) does not say...

src/apprt/embedded.zig

+3-3
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ pub const CAPI = struct {
508508
action,
509509
key,
510510
unmapped_key,
511-
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(mods))))),
511+
@bitCast(@as(input.Mods.Int, @truncate(@as(c_uint, @bitCast(mods))))),
512512
);
513513
}
514514

@@ -527,7 +527,7 @@ pub const CAPI = struct {
527527
surface.mouseButtonCallback(
528528
action,
529529
button,
530-
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(mods))))),
530+
@bitCast(@as(input.Mods.Int, @truncate(@as(c_uint, @bitCast(mods))))),
531531
);
532532
}
533533

@@ -545,7 +545,7 @@ pub const CAPI = struct {
545545
surface.scrollCallback(
546546
x,
547547
y,
548-
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
548+
@bitCast(@as(input.ScrollMods.Int, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
549549
);
550550
}
551551

src/apprt/glfw.zig

+13-2
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ pub const Surface = struct {
564564
defer tracy.end();
565565

566566
// Convert our glfw types into our input types
567-
const mods: input.Mods = @bitCast(glfw_mods);
567+
const mods = convertMods(glfw_mods);
568568
const action: input.Action = switch (glfw_action) {
569569
.release => .release,
570570
.press => .press,
@@ -784,7 +784,7 @@ pub const Surface = struct {
784784
const core_win = window.getUserPointer(CoreSurface) orelse return;
785785

786786
// Convert glfw button to input button
787-
const mods: input.Mods = @bitCast(glfw_mods);
787+
const mods = convertMods(glfw_mods);
788788
const button: input.MouseButton = switch (glfw_button) {
789789
.left => .left,
790790
.right => .right,
@@ -806,4 +806,15 @@ pub const Surface = struct {
806806
return;
807807
};
808808
}
809+
810+
fn convertMods(mods: glfw.Mods) input.Mods {
811+
return .{
812+
.shift = if (mods.shift) .both else .none,
813+
.ctrl = if (mods.control) .both else .none,
814+
.alt = if (mods.alt) .both else .none,
815+
.super = if (mods.super) .both else .none,
816+
.caps_lock = mods.caps_lock,
817+
.num_lock = mods.num_lock,
818+
};
819+
}
809820
};

src/apprt/gtk.zig

+4-4
Original file line numberDiff line numberDiff line change
@@ -1230,10 +1230,10 @@ fn translateMouseButton(button: c.guint) input.MouseButton {
12301230

12311231
fn translateMods(state: c.GdkModifierType) input.Mods {
12321232
var mods: input.Mods = .{};
1233-
if (state & c.GDK_SHIFT_MASK != 0) mods.shift = true;
1234-
if (state & c.GDK_CONTROL_MASK != 0) mods.ctrl = true;
1235-
if (state & c.GDK_ALT_MASK != 0) mods.alt = true;
1236-
if (state & c.GDK_SUPER_MASK != 0) mods.super = true;
1233+
if (state & c.GDK_SHIFT_MASK != 0) mods.shift = .both;
1234+
if (state & c.GDK_CONTROL_MASK != 0) mods.ctrl = .both;
1235+
if (state & c.GDK_ALT_MASK != 0) mods.alt = .both;
1236+
if (state & c.GDK_SUPER_MASK != 0) mods.super = .both;
12371237

12381238
// Lock is dependent on the X settings but we just assume caps lock.
12391239
if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true;

0 commit comments

Comments
 (0)