mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: proper event when sided mod released with other side pressed
Related to https://github.com/mitchellh/ghostty/issues/1082 This fixes two separate issues to follow along with the new spec changes Kovid pushed to Kitty: 1. When two modifiers are pressed and one is released, this shows up as a proper release event with the correct side. Previously, the correct side was shown but as a press event. 2. When two modifiers are pressed and one is released, the Kitty event should not have that specific modifier set. For example, pressing left ctrl, then right ctrl, then releasing right ctrl should encode as "right ctrl released" but with NO modifiers still present.
This commit is contained in:
@ -905,10 +905,32 @@ extension Ghostty {
|
||||
// but this is super cheap and flagsChanged isn't that common.
|
||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
|
||||
// If the key that pressed this is active, its a press, else release
|
||||
// If the key that pressed this is active, its a press, else release.
|
||||
var action = GHOSTTY_ACTION_RELEASE
|
||||
if (mods.rawValue & mod != 0) { action = GHOSTTY_ACTION_PRESS }
|
||||
|
||||
if (mods.rawValue & mod != 0) {
|
||||
// If the key is pressed, its slightly more complicated, because we
|
||||
// want to check if the pressed modifier is the correct side. If the
|
||||
// correct side is pressed then its a press event otherwise its a release
|
||||
// event with the opposite modifier still held.
|
||||
let sidePressed: Bool
|
||||
switch (event.keyCode) {
|
||||
case 0x3C:
|
||||
sidePressed = event.modifierFlags.rawValue & UInt(NX_DEVICERSHIFTKEYMASK) != 0;
|
||||
case 0x3E:
|
||||
sidePressed = event.modifierFlags.rawValue & UInt(NX_DEVICERCTLKEYMASK) != 0;
|
||||
case 0x3D:
|
||||
sidePressed = event.modifierFlags.rawValue & UInt(NX_DEVICERALTKEYMASK) != 0;
|
||||
case 0x36:
|
||||
sidePressed = event.modifierFlags.rawValue & UInt(NX_DEVICERCMDKEYMASK) != 0;
|
||||
default:
|
||||
sidePressed = true
|
||||
}
|
||||
|
||||
if (sidePressed) {
|
||||
action = GHOSTTY_ACTION_PRESS
|
||||
}
|
||||
}
|
||||
|
||||
keyAction(action, event: event)
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,11 @@ fn kitty(
|
||||
var seq: KittySequence = .{
|
||||
.key = entry.code,
|
||||
.final = entry.final,
|
||||
.mods = KittyMods.fromInput(all_mods),
|
||||
.mods = KittyMods.fromInput(
|
||||
self.event.action,
|
||||
self.event.key,
|
||||
all_mods,
|
||||
),
|
||||
};
|
||||
|
||||
if (self.kitty_flags.report_events) {
|
||||
@ -596,12 +600,27 @@ const KittyMods = packed struct(u8) {
|
||||
num_lock: bool = false,
|
||||
|
||||
/// Convert an input mods value into the CSI u mods value.
|
||||
pub fn fromInput(mods: key.Mods) KittyMods {
|
||||
pub fn fromInput(
|
||||
action: key.Action,
|
||||
k: key.Key,
|
||||
mods: key.Mods,
|
||||
) KittyMods {
|
||||
// Annoying boolean logic, but according to the Kitty spec:
|
||||
// "When both left and right control keys are pressed and one is
|
||||
// released, the release event must again have the modifier bit reset"
|
||||
// In other words, we allow a modifier if it is set AND the action
|
||||
// is NOT a release. Or if the action is a release, then the key being
|
||||
// released must not be the associated modifier key.
|
||||
const shift = mods.shift and (action != .release or (k != .left_shift and k != .right_shift));
|
||||
const alt = mods.alt and (action != .release or (k != .left_alt and k != .right_alt));
|
||||
const ctrl = mods.ctrl and (action != .release or (k != .left_control and k != .right_control));
|
||||
const super = mods.super and (action != .release or (k != .left_super and k != .right_super));
|
||||
|
||||
return .{
|
||||
.shift = mods.shift,
|
||||
.alt = mods.alt,
|
||||
.ctrl = mods.ctrl,
|
||||
.super = mods.super,
|
||||
.shift = shift,
|
||||
.alt = alt,
|
||||
.ctrl = ctrl,
|
||||
.super = super,
|
||||
.caps_lock = mods.caps_lock,
|
||||
.num_lock = mods.num_lock,
|
||||
};
|
||||
@ -982,6 +1001,27 @@ test "kitty: ctrl with all flags" {
|
||||
try testing.expectEqualStrings("[57442;5u", actual[1..]);
|
||||
}
|
||||
|
||||
test "kitty: ctrl release with ctrl mod set" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var enc: KeyEncoder = .{
|
||||
.event = .{
|
||||
.action = .release,
|
||||
.key = .left_control,
|
||||
.mods = .{ .ctrl = true },
|
||||
.utf8 = "",
|
||||
},
|
||||
.kitty_flags = .{
|
||||
.disambiguate = true,
|
||||
.report_events = true,
|
||||
.report_alternates = true,
|
||||
.report_all = true,
|
||||
.report_associated = true,
|
||||
},
|
||||
};
|
||||
const actual = try enc.kitty(&buf);
|
||||
try testing.expectEqualStrings("[57442;1:3u", actual[1..]);
|
||||
}
|
||||
|
||||
test "kitty: delete" {
|
||||
var buf: [128]u8 = undefined;
|
||||
{
|
||||
|
Reference in New Issue
Block a user