Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
//! Keyboard input handling for Wayland via the RemoteDesktop portal.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use ashpd::desktop::Session;
|
||||
use ashpd::desktop::remote_desktop::{KeyState, RemoteDesktop};
|
||||
|
||||
use super::super::keysym::{XK_SHIFT_L, char_to_keysym, keysym_needs_shift};
|
||||
use crate::Key;
|
||||
|
||||
/// Keyboard state for tracking auto-shifted keys.
|
||||
pub struct Keyboard {
|
||||
/// Keysyms currently held that required auto-shift.
|
||||
/// When non-empty, shift is being held by us.
|
||||
auto_shift_keys: HashSet<i32>,
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
auto_shift_keys: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Types a string of text by sending keysym events.
|
||||
pub async fn type_text<'a>(
|
||||
&mut self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
text: &str,
|
||||
) -> Result<(), String> {
|
||||
for ch in text.chars() {
|
||||
self.type_char(remote_desktop, session, ch).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a key down event for the given key.
|
||||
///
|
||||
/// For `Key::Char`, this will automatically press shift if needed.
|
||||
pub async fn key_down<'a>(
|
||||
&mut self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
key: &Key,
|
||||
) -> Result<(), String> {
|
||||
let (keysym, needs_shift) = self.resolve_key(key);
|
||||
|
||||
if needs_shift {
|
||||
// Press shift only if this is the first auto-shifted key.
|
||||
if self.auto_shift_keys.is_empty() {
|
||||
self.press_shift(remote_desktop, session).await?;
|
||||
}
|
||||
self.auto_shift_keys.insert(keysym);
|
||||
}
|
||||
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, keysym, KeyState::Pressed)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send key down: {e}"))
|
||||
}
|
||||
|
||||
/// Sends a key up event for the given key.
|
||||
///
|
||||
/// For `Key::Char`, this will automatically release shift if it was auto-pressed
|
||||
/// and this is the last auto-shifted key being released.
|
||||
pub async fn key_up<'a>(
|
||||
&mut self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
key: &Key,
|
||||
) -> Result<(), String> {
|
||||
let (keysym, _) = self.resolve_key(key);
|
||||
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, keysym, KeyState::Released)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send key up: {e}"))?;
|
||||
|
||||
// Release shift only if this key was auto-shifted and it's the last one.
|
||||
if self.auto_shift_keys.remove(&keysym) && self.auto_shift_keys.is_empty() {
|
||||
self.release_shift(remote_desktop, session).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn type_char<'a>(
|
||||
&mut self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
ch: char,
|
||||
) -> Result<(), String> {
|
||||
let keysym = char_to_keysym(ch) as i32;
|
||||
let needs_shift = keysym_needs_shift(keysym as u32);
|
||||
|
||||
// Press shift if needed, then press and release the key, then release shift.
|
||||
if needs_shift {
|
||||
self.press_shift(remote_desktop, session).await?;
|
||||
}
|
||||
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, keysym, KeyState::Pressed)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send key press: {e}"))?;
|
||||
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, keysym, KeyState::Released)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send key release: {e}"))?;
|
||||
|
||||
if needs_shift {
|
||||
self.release_shift(remote_desktop, session).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolves a `Key` to a keysym and shift requirement.
|
||||
fn resolve_key(&self, key: &Key) -> (i32, bool) {
|
||||
match key {
|
||||
Key::Keycode(keysym) => {
|
||||
// Key::Keycode uses X11 keysyms, which we can use directly.
|
||||
(*keysym, false)
|
||||
}
|
||||
Key::Char(ch) => {
|
||||
let keysym = char_to_keysym(*ch) as i32;
|
||||
let needs_shift = keysym_needs_shift(keysym as u32);
|
||||
(keysym, needs_shift)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn press_shift<'a>(
|
||||
&self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
) -> Result<(), String> {
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, XK_SHIFT_L as i32, KeyState::Pressed)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to press shift: {e}"))
|
||||
}
|
||||
|
||||
async fn release_shift<'a>(
|
||||
&self,
|
||||
remote_desktop: &RemoteDesktop<'a>,
|
||||
session: &Session<'a, RemoteDesktop<'a>>,
|
||||
) -> Result<(), String> {
|
||||
remote_desktop
|
||||
.notify_keyboard_keysym(session, XK_SHIFT_L as i32, KeyState::Released)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to release shift: {e}"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user