Fix Right Alt key not working in keybindings on Windows (#40536)

joel created

### Problem
On Windows, the right Alt key was not working in keybindings (e.g.,
`Ctrl+Right Alt+B`), while the left Alt key worked correctly. This was
due to overly aggressive AltGr detection that treated any `right Alt +
left Ctrl` combination as AltGr, even on US keyboards where AltGr
doesn't exist.

### Root Cause
Windows internally represents AltGr (Alt Graph) as `right Alt + left
Ctrl` pressed simultaneously. The previous implementation always
excluded this combination from being treated as regular modifier keys to
support international keyboards. However, this broke keybindings using
right Alt on US/UK keyboards where users expect right Alt to behave
identically to left Alt.

### Solution
Implemented keyboard layout-aware AltGr detection:

1. Added `uses_altgr()` method to `WindowsKeyboardLayout` that checks if
the current keyboard layout is known to use AltGr (German, French,
Spanish, Polish, etc.)
2. Modified `current_modifiers()` to only apply AltGr special handling
when the keyboard layout actually uses it
3. Added explicit checking for both `VK_LMENU` and `VK_RMENU` instead of
relying solely on the generic `VK_MENU`

### Behavior
- **US/UK keyboards**: Right Alt now works identically to left Alt in
keybindings. `Ctrl+Right Alt+B` triggers the same action as `Ctrl+Left
Alt+B`
- **International keyboards** (German, French, Spanish, etc.): AltGr
continues to work correctly for typing special characters and doesn't
trigger keybindings
- **All keyboards**: Both Alt keys are detected symmetrically, matching
the behavior of left/right Windows keys

### Testing
Manually tested on Windows with US keyboard layout:
-  `Ctrl+Left Alt+B` triggers keybinding
-  `Ctrl+Right Alt+B` triggers keybinding  
-  Both Alt keys work independently in keybindings


Release Notes:
- Fixed Right Alt key not working in keybindings on Windows

Change summary

crates/gpui/src/platform/windows/events.rs   | 20 +++++++++++--
crates/gpui/src/platform/windows/keyboard.rs | 32 ++++++++++++++++++++++
2 files changed, 48 insertions(+), 4 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/events.rs 🔗

@@ -1307,10 +1307,10 @@ where
     F: FnOnce(Keystroke) -> PlatformInput,
 {
     let virtual_key = VIRTUAL_KEY(wparam.loword());
-    let mut modifiers = current_modifiers();
+    let modifiers = current_modifiers();
 
     match virtual_key {
-        VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => {
+        VK_SHIFT | VK_CONTROL | VK_MENU | VK_LMENU | VK_RMENU | VK_LWIN | VK_RWIN => {
             if state
                 .last_reported_modifiers
                 .is_some_and(|prev_modifiers| prev_modifiers == modifiers)
@@ -1460,13 +1460,25 @@ fn is_virtual_key_pressed(vkey: VIRTUAL_KEY) -> bool {
     unsafe { GetKeyState(vkey.0 as i32) < 0 }
 }
 
+fn keyboard_uses_altgr() -> bool {
+    use crate::platform::windows::keyboard::WindowsKeyboardLayout;
+    WindowsKeyboardLayout::new()
+        .map(|layout| layout.uses_altgr())
+        .unwrap_or(false)
+}
+
 #[inline]
 pub(crate) fn current_modifiers() -> Modifiers {
-    let altgr = is_virtual_key_pressed(VK_RMENU) && is_virtual_key_pressed(VK_LCONTROL);
+    let lmenu_pressed = is_virtual_key_pressed(VK_LMENU);
+    let rmenu_pressed = is_virtual_key_pressed(VK_RMENU);
+    let lcontrol_pressed = is_virtual_key_pressed(VK_LCONTROL);
+
+    // Only treat right Alt + left Ctrl as AltGr on keyboards that actually use it
+    let altgr = keyboard_uses_altgr() && rmenu_pressed && lcontrol_pressed;
 
     Modifiers {
         control: is_virtual_key_pressed(VK_CONTROL) && !altgr,
-        alt: is_virtual_key_pressed(VK_MENU) && !altgr,
+        alt: (lmenu_pressed || rmenu_pressed) && !altgr,
         shift: is_virtual_key_pressed(VK_SHIFT),
         platform: is_virtual_key_pressed(VK_LWIN) || is_virtual_key_pressed(VK_RWIN),
         function: false,

crates/gpui/src/platform/windows/keyboard.rs 🔗

@@ -110,6 +110,38 @@ impl WindowsKeyboardLayout {
             name: "unknown".to_string(),
         }
     }
+
+    pub(crate) fn uses_altgr(&self) -> bool {
+        // Check if this is a known AltGr layout by examining the layout ID
+        // The layout ID is a hex string like "00000409" (US) or "00000407" (German)
+        // Extract the language ID (last 4 bytes)
+        let id_bytes = self.id.as_bytes();
+        if id_bytes.len() >= 4 {
+            let lang_id = &id_bytes[id_bytes.len() - 4..];
+            // List of keyboard layouts that use AltGr (non-exhaustive)
+            matches!(
+                lang_id,
+                b"0407" | // German
+                b"040C" | // French
+                b"040A" | // Spanish
+                b"0415" | // Polish
+                b"0413" | // Dutch
+                b"0816" | // Portuguese
+                b"041D" | // Swedish
+                b"0414" | // Norwegian
+                b"040B" | // Finnish
+                b"041F" | // Turkish
+                b"0419" | // Russian
+                b"0405" | // Czech
+                b"040E" | // Hungarian
+                b"0424" | // Slovenian
+                b"041B" | // Slovak
+                b"0418" // Romanian
+            )
+        } else {
+            false
+        }
+    }
 }
 
 impl WindowsKeyboardMapper {