first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
@@ -2,7 +2,6 @@ use std::borrow::Cow;
use std::collections::HashMap;
use lazy_static::lazy_static;
use winit::event::ElementState;
#[cfg(windows)]
use winit::keyboard::NativeKey;
@@ -10,10 +9,10 @@ use winit::keyboard::{Key, ModifiersState, NamedKey};
#[cfg(not(target_family = "wasm"))]
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
use crate::platform::KEYS_TO_IGNORE;
use crate::{event::KeyEventDetails, keymap::Keystroke};
use super::WindowState;
use crate::event::KeyEventDetails;
use crate::keymap::Keystroke;
use crate::platform::KEYS_TO_IGNORE;
lazy_static! {
/// Mapping between a printable ASCII character and its corresponding control code had `ctrl`
@@ -101,6 +100,25 @@ pub fn convert_keyboard_input_event(
{
input.key_without_modifiers()
}
// On Windows, non-Latin keyboard layouts (Cyrillic, Greek, Arabic, etc.) translate
// the physical key to a non-ASCII character even when Ctrl/Cmd is held. That makes
// bindings like `ctrl-c` / `ctrl-v` fail to match. Fall back to the US-QWERTY
// position so chord shortcuts work regardless of the active layout — same approach
// used by VS Code, JetBrains, and Chromium. Issue #9036.
//
// Right-Alt is excluded because Windows reports AltGr as Ctrl+Alt; without this
// guard, AltGr-produced characters (e.g. `€` on a German layout) would be rewritten
// into a spurious chord and the typed character would be swallowed.
#[cfg(windows)]
Key::Character(c)
if (window_state.modifiers.control_key() || window_state.modifiers.super_key())
&& !window_state.right_alt_pressed
&& !c.is_ascii() =>
{
us_qwerty_fallback_for_chord(&input.physical_key, shift)
.map(|s| Key::Character(s.into()))
.unwrap_or_else(|| input.logical_key.clone())
}
_ => input.logical_key,
};
let input_key = get_input_key(&logical_key, shift);
@@ -253,6 +271,100 @@ fn convert_key(key: Key) -> Option<Cow<'static, str>> {
Some(Cow::Borrowed(value))
}
/// Maps a winit `PhysicalKey` to the US-QWERTY character it would produce. Used on Windows
/// to recover layout-independent chord shortcuts (e.g. `ctrl-c`) when a non-Latin keyboard
/// layout has translated the logical key to a non-ASCII character.
///
/// Returns `None` for keys outside the standard letter/digit/punctuation set (function keys,
/// modifiers, navigation keys, etc.), since those either aren't typically used in chord
/// bindings as character keys or are already handled via `NamedKey` in `convert_key`.
#[cfg_attr(not(windows), allow(dead_code))]
fn us_qwerty_fallback_for_chord(
physical_key: &winit::keyboard::PhysicalKey,
shift: bool,
) -> Option<&'static str> {
use winit::keyboard::{KeyCode, PhysicalKey};
let PhysicalKey::Code(code) = physical_key else {
return None;
};
// Letters always return lowercase here; `get_input_key` applies the uppercase
// transform downstream when shift is held. Digit/punctuation keys must return
// the shifted US-QWERTY symbol up front because `get_input_key` passes
// non-letter characters through unchanged, so bindings like `ctrl-shift-}`
// would otherwise see `ctrl-shift-]` on non-Latin layouts.
Some(match (code, shift) {
(KeyCode::KeyA, _) => "a",
(KeyCode::KeyB, _) => "b",
(KeyCode::KeyC, _) => "c",
(KeyCode::KeyD, _) => "d",
(KeyCode::KeyE, _) => "e",
(KeyCode::KeyF, _) => "f",
(KeyCode::KeyG, _) => "g",
(KeyCode::KeyH, _) => "h",
(KeyCode::KeyI, _) => "i",
(KeyCode::KeyJ, _) => "j",
(KeyCode::KeyK, _) => "k",
(KeyCode::KeyL, _) => "l",
(KeyCode::KeyM, _) => "m",
(KeyCode::KeyN, _) => "n",
(KeyCode::KeyO, _) => "o",
(KeyCode::KeyP, _) => "p",
(KeyCode::KeyQ, _) => "q",
(KeyCode::KeyR, _) => "r",
(KeyCode::KeyS, _) => "s",
(KeyCode::KeyT, _) => "t",
(KeyCode::KeyU, _) => "u",
(KeyCode::KeyV, _) => "v",
(KeyCode::KeyW, _) => "w",
(KeyCode::KeyX, _) => "x",
(KeyCode::KeyY, _) => "y",
(KeyCode::KeyZ, _) => "z",
(KeyCode::Digit1, true) => "!",
(KeyCode::Digit1, false) => "1",
(KeyCode::Digit2, true) => "@",
(KeyCode::Digit2, false) => "2",
(KeyCode::Digit3, true) => "#",
(KeyCode::Digit3, false) => "3",
(KeyCode::Digit4, true) => "$",
(KeyCode::Digit4, false) => "4",
(KeyCode::Digit5, true) => "%",
(KeyCode::Digit5, false) => "5",
(KeyCode::Digit6, true) => "^",
(KeyCode::Digit6, false) => "6",
(KeyCode::Digit7, true) => "&",
(KeyCode::Digit7, false) => "7",
(KeyCode::Digit8, true) => "*",
(KeyCode::Digit8, false) => "8",
(KeyCode::Digit9, true) => "(",
(KeyCode::Digit9, false) => "9",
(KeyCode::Digit0, true) => ")",
(KeyCode::Digit0, false) => "0",
(KeyCode::Minus, true) => "_",
(KeyCode::Minus, false) => "-",
(KeyCode::Equal, true) => "+",
(KeyCode::Equal, false) => "=",
(KeyCode::BracketLeft, true) => "{",
(KeyCode::BracketLeft, false) => "[",
(KeyCode::BracketRight, true) => "}",
(KeyCode::BracketRight, false) => "]",
(KeyCode::Backslash, true) => "|",
(KeyCode::Backslash, false) => "\\",
(KeyCode::Semicolon, true) => ":",
(KeyCode::Semicolon, false) => ";",
(KeyCode::Quote, true) => "\"",
(KeyCode::Quote, false) => "'",
(KeyCode::Comma, true) => "<",
(KeyCode::Comma, false) => ",",
(KeyCode::Period, true) => ">",
(KeyCode::Period, false) => ".",
(KeyCode::Slash, true) => "?",
(KeyCode::Slash, false) => "/",
(KeyCode::Backquote, true) => "~",
(KeyCode::Backquote, false) => "`",
_ => return None,
})
}
#[cfg(test)]
#[path = "key_events_tests.rs"]
mod tests;