Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
use std::collections::HashMap;
use objc2_core_graphics::{
CGEvent, CGEventFlags, CGEventSource, CGEventSourceStateID, CGEventTapLocation, CGKeyCode,
};
use super::keycode_cache;
use crate::Key;
/// Manages keyboard state and posts keyboard events to the system.
pub struct Keyboard {
/// Cache of character-to-keycode mappings for the current keyboard layout.
cache: HashMap<char, CGKeyCode>,
}
impl Keyboard {
pub fn new() -> Self {
Self {
cache: keycode_cache::build_cache(),
}
}
/// Sends a key down event for the given key.
pub fn key_down(&self, key: &Key) -> Result<(), String> {
post_key_down(self.resolve_keycode(key)?)
}
/// Sends a key up event for the given key.
pub fn key_up(&self, key: &Key) -> Result<(), String> {
post_key_up(self.resolve_keycode(key)?)
}
/// Simulates typing text by sending Quartz events.
pub fn type_text(&self, text: &str) -> Result<(), String> {
let source = CGEventSource::new(CGEventSourceStateID::CombinedSessionState);
// Send one character at a time for better compatibility with various applications.
for ch in text.chars() {
// For now, send each character using the unicode method. This is easier than using
// virtual key codes, but may not be supported in all applications.
//
// TODO(vorporeal): when sending an ASCII character, send it using virtual key codes
// for better compatibility.
type_unicode_char(ch, source.as_deref())?;
}
Ok(())
}
/// Resolves a Key to a CGKeyCode.
///
/// The key can be:
/// - A keycode (platform-specific virtual keycode)
/// - A character (looked up via the current keyboard layout)
fn resolve_keycode(&self, key: &Key) -> Result<CGKeyCode, String> {
match key {
Key::Keycode(code) => CGKeyCode::try_from(*code).map_err(|_| {
format!(
"Invalid keycode {code}: must be in range 0..={}",
CGKeyCode::MAX
)
}),
Key::Char(ch) => self
.cache
.get(ch)
.copied()
.ok_or_else(|| format!("No keycode found for character '{}'", ch)),
}
}
}
/// Posts a key down event for the given virtual keycode.
fn post_key_down(keycode: CGKeyCode) -> Result<(), String> {
let source = CGEventSource::new(CGEventSourceStateID::CombinedSessionState);
let event = CGEvent::new_keyboard_event(source.as_deref(), keycode, true)
.ok_or_else(|| format!("Failed to create key down event for keycode {}", keycode))?;
CGEvent::post(CGEventTapLocation::HIDEventTap, Some(&event));
Ok(())
}
/// Posts a key up event for the given virtual keycode.
fn post_key_up(keycode: CGKeyCode) -> Result<(), String> {
let source = CGEventSource::new(CGEventSourceStateID::CombinedSessionState);
let event = CGEvent::new_keyboard_event(source.as_deref(), keycode, false)
.ok_or_else(|| format!("Failed to create key up event for keycode {}", keycode))?;
CGEvent::post(CGEventTapLocation::HIDEventTap, Some(&event));
Ok(())
}
/// Generates a Quartz event signifying the typing of a single Unicode character.
fn type_unicode_char(ch: char, source: Option<&CGEventSource>) -> Result<(), String> {
let mut buf = [0u16; 2];
let encoded = ch.encode_utf16(&mut buf);
// Create a key down event (virtual key code 0 is used as a placeholder).
let key_down = CGEvent::new_keyboard_event(source, 0, true)
.ok_or("Failed to create key down event for TypeText.")?;
// Set the unicode string on the event.
// Safety: encoded is a valid UTF-16 buffer with the correct length.
unsafe {
CGEvent::keyboard_set_unicode_string(
Some(&key_down),
encoded.len() as u64,
encoded.as_ptr(),
);
}
// Clear any modifier flags that might interfere.
CGEvent::set_flags(Some(&key_down), CGEventFlags::empty());
// Post the key down event.
CGEvent::post(CGEventTapLocation::HIDEventTap, Some(&key_down));
// Create and post a corresponding key up event.
let key_up = CGEvent::new_keyboard_event(source, 0, false)
.ok_or("Failed to create key up event for TypeText.")?;
CGEvent::set_flags(Some(&key_up), CGEventFlags::empty());
CGEvent::post(CGEventTapLocation::HIDEventTap, Some(&key_up));
Ok(())
}