Handle super+period key combination on macOS

Previously on macOS, `super+period` key combination was being blocked
in `performKeyEquivalent` due to a control modifier check. This change
removes that restriction to allow `super+period` and similar key
combinations to be properly handled.

Fixes #4540
This commit is contained in:
Bryan Lee
2025-01-04 15:00:04 +08:00
parent 1baf8928a0
commit dde1ae51bd

View File

@ -810,49 +810,65 @@ extension Ghostty {
return false return false
} }
// Only process keys when Control is active. All known issues we're // First check if this is a special key combination we want to handle
// resolving happen only in this scenario. This probably isn't fully robust // NOTE: We used to only process Control key combinations here as a defensive measure.
// but we can broaden the scope as we find more cases. // This has been removed to support more key combinations (like super+period),
if (!event.modifierFlags.contains(.control)) { // but we need to be careful about potential conflicts with system shortcuts.
return false // Each key combination should be explicitly checked in the switch statement below.
} let (shouldHandle, equivalent): (Bool, String?) = {
let equivalent: String
switch (event.charactersIgnoringModifiers) { switch (event.charactersIgnoringModifiers) {
case "/": case "/":
// Treat C-/ as C-_. We do this because C-/ makes macOS make a beep // Treat C-/ as C-_. We do this because C-/ makes macOS make a beep
// sound and we don't like the beep sound. // sound and we don't like the beep sound.
if (!event.modifierFlags.contains(.control) || if (event.modifierFlags.contains(.control) &&
!event.modifierFlags.isDisjoint(with: [.shift, .command, .option])) { event.modifierFlags.isDisjoint(with: [.shift, .command, .option])) {
return false return (true, "_")
} }
equivalent = "_"
case "\r": case "\r":
// Pass C-<return> through verbatim // Pass C-<return> through verbatim
// (prevent the default context menu equivalent) // (prevent the default context menu equivalent)
equivalent = "\r" if (event.modifierFlags.contains(.control)) {
return (true, "\r")
}
case ".":
// Handle super+period
if (event.modifierFlags.contains(.command)) {
return (true, ".")
}
default: default:
// Ignore other events break
}
return (false, nil)
}()
// If we don't handle this combination, return false
if (!shouldHandle) {
return false return false
} }
let newEvent = NSEvent.keyEvent( // Create a new event if we need to change the character
let finalEvent: NSEvent
if let eq = equivalent, eq != event.charactersIgnoringModifiers {
finalEvent = NSEvent.keyEvent(
with: .keyDown, with: .keyDown,
location: event.locationInWindow, location: event.locationInWindow,
modifierFlags: event.modifierFlags, modifierFlags: event.modifierFlags,
timestamp: event.timestamp, timestamp: event.timestamp,
windowNumber: event.windowNumber, windowNumber: event.windowNumber,
context: nil, context: nil,
characters: equivalent, characters: eq,
charactersIgnoringModifiers: equivalent, charactersIgnoringModifiers: eq,
isARepeat: event.isARepeat, isARepeat: event.isARepeat,
keyCode: event.keyCode keyCode: event.keyCode
) ) ?? event
} else {
finalEvent = event
}
self.keyDown(with: newEvent!) self.keyDown(with: finalEvent)
return true return true
} }