Don't insert input when the `fn` key is held

Antonio Scandurra created

Change summary

crates/gpui/src/keymap.rs              |  7 +++++++
crates/gpui/src/platform/mac/event.rs  | 18 ++++++++++++------
crates/gpui/src/platform/mac/window.rs | 22 ++++++++++++++--------
crates/terminal/src/mappings/keys.rs   |  1 +
4 files changed, 34 insertions(+), 14 deletions(-)

Detailed changes

crates/gpui/src/keymap.rs 🔗

@@ -41,6 +41,7 @@ pub struct Keystroke {
     pub alt: bool,
     pub shift: bool,
     pub cmd: bool,
+    pub function: bool,
     pub key: String,
 }
 
@@ -277,6 +278,7 @@ impl Keystroke {
         let mut alt = false;
         let mut shift = false;
         let mut cmd = false;
+        let mut function = false;
         let mut key = None;
 
         let mut components = source.split("-").peekable();
@@ -286,6 +288,7 @@ impl Keystroke {
                 "alt" => alt = true,
                 "shift" => shift = true,
                 "cmd" => cmd = true,
+                "fn" => function = true,
                 _ => {
                     if let Some(component) = components.peek() {
                         if component.is_empty() && source.ends_with('-') {
@@ -306,6 +309,7 @@ impl Keystroke {
             alt,
             shift,
             cmd,
+            function,
             key: key.unwrap(),
         })
     }
@@ -464,6 +468,7 @@ mod tests {
                 alt: false,
                 shift: false,
                 cmd: false,
+                function: false,
             }
         );
 
@@ -475,6 +480,7 @@ mod tests {
                 alt: true,
                 shift: true,
                 cmd: false,
+                function: false,
             }
         );
 
@@ -486,6 +492,7 @@ mod tests {
                 alt: false,
                 shift: true,
                 cmd: true,
+                function: false,
             }
         );
 

crates/gpui/src/platform/mac/event.rs 🔗

@@ -210,19 +210,24 @@ impl Event {
 unsafe fn parse_keystroke(native_event: id) -> Keystroke {
     use cocoa::appkit::*;
 
+    let mut chars_ignoring_modifiers =
+        CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
+            .to_str()
+            .unwrap();
+    let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16);
     let modifiers = native_event.modifierFlags();
+
     let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
     let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
     let mut shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
     let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
-
-    let mut chars_ignoring_modifiers =
-        CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
-            .to_str()
-            .unwrap();
+    let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask)
+        && first_char.map_or(true, |ch| {
+            ch < NSUpArrowFunctionKey || ch > NSModeSwitchFunctionKey
+        });
 
     #[allow(non_upper_case_globals)]
-    let key = match chars_ignoring_modifiers.chars().next().map(|ch| ch as u16) {
+    let key = match first_char {
         Some(SPACE_KEY) => "space",
         Some(BACKSPACE_KEY) => "backspace",
         Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter",
@@ -282,6 +287,7 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
         alt,
         shift,
         cmd,
+        function,
         key: key.into(),
     }
 }

crates/gpui/src/platform/mac/window.rs 🔗

@@ -711,14 +711,16 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
     let window_state = unsafe { get_window_state(this) };
 
     let mut window_state_borrow = window_state.as_ref().borrow_mut();
-    if key_equivalent {
-        window_state_borrow.performed_key_equivalent = true;
-    } else if window_state_borrow.performed_key_equivalent {
-        return NO;
-    }
 
     let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
     if let Some(event) = event {
+        if key_equivalent {
+            window_state_borrow.performed_key_equivalent = true;
+        } else if window_state_borrow.performed_key_equivalent {
+            return NO;
+        }
+
+        let function_is_held;
         window_state_borrow.pending_key_down = match event {
             Event::KeyDown(event) => {
                 let keydown = event.keystroke.clone();
@@ -732,15 +734,18 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
                     window_state_borrow.last_fresh_keydown = Some(keydown);
                 }
 
+                function_is_held = event.keystroke.function;
                 Some((event, None))
             }
             _ => return NO,
         };
         drop(window_state_borrow);
 
-        unsafe {
-            let input_context: id = msg_send![this, inputContext];
-            let _: BOOL = msg_send![input_context, handleEvent: native_event];
+        if !function_is_held {
+            unsafe {
+                let input_context: id = msg_send![this, inputContext];
+                let _: BOOL = msg_send![input_context, handleEvent: native_event];
+            }
         }
 
         let mut handled = false;
@@ -856,6 +861,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
         ctrl: false,
         alt: false,
         shift: false,
+        function: false,
         key: ".".into(),
     };
     let event = Event::KeyDown(KeyDownEvent {

crates/terminal/src/mappings/keys.rs 🔗

@@ -311,6 +311,7 @@ mod test {
             alt: false,
             shift: false,
             cmd: false,
+            function: false,
             key: "🖖🏻".to_string(), //2 char string
         };
         assert_eq!(to_esc_str(&ks, &TermMode::NONE), None);