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?) = {
switch (event.charactersIgnoringModifiers) {
case "/":
// Treat C-/ as C-_. We do this because C-/ makes macOS make a beep
// sound and we don't like the beep sound.
if (event.modifierFlags.contains(.control) &&
event.modifierFlags.isDisjoint(with: [.shift, .command, .option])) {
return (true, "_")
}
let equivalent: String case "\r":
switch (event.charactersIgnoringModifiers) { // Pass C-<return> through verbatim
case "/": // (prevent the default context menu equivalent)
// Treat C-/ as C-_. We do this because C-/ makes macOS make a beep if (event.modifierFlags.contains(.control)) {
// sound and we don't like the beep sound. return (true, "\r")
if (!event.modifierFlags.contains(.control) || }
!event.modifierFlags.isDisjoint(with: [.shift, .command, .option])) {
return false case ".":
// Handle super+period
if (event.modifierFlags.contains(.command)) {
return (true, ".")
}
default:
break
} }
return (false, nil)
}()
equivalent = "_" // If we don't handle this combination, return false
if (!shouldHandle) {
case "\r":
// Pass C-<return> through verbatim
// (prevent the default context menu equivalent)
equivalent = "\r"
default:
// Ignore other events
return false return false
} }
let newEvent = NSEvent.keyEvent( // Create a new event if we need to change the character
with: .keyDown, let finalEvent: NSEvent
location: event.locationInWindow, if let eq = equivalent, eq != event.charactersIgnoringModifiers {
modifierFlags: event.modifierFlags, finalEvent = NSEvent.keyEvent(
timestamp: event.timestamp, with: .keyDown,
windowNumber: event.windowNumber, location: event.locationInWindow,
context: nil, modifierFlags: event.modifierFlags,
characters: equivalent, timestamp: event.timestamp,
charactersIgnoringModifiers: equivalent, windowNumber: event.windowNumber,
isARepeat: event.isARepeat, context: nil,
keyCode: event.keyCode characters: eq,
) charactersIgnoringModifiers: eq,
isARepeat: event.isARepeat,
keyCode: event.keyCode
) ?? event
} else {
finalEvent = event
}
self.keyDown(with: newEvent!) self.keyDown(with: finalEvent)
return true return true
} }