Skip to main content
Glama

Edit-MCP

input.rs22.5 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. //! Parses VT sequences into input events. //! //! In the future this allows us to take apart the application and //! support input schemes that aren't VT, such as UEFI, or GUI. use crate::helpers::{CoordType, Point, Size}; use crate::vt; /// Represents a key/modifier combination. /// /// TODO: Is this a good idea? I did it to allow typing `kbmod::CTRL | vk::A`. /// The reason it's an awkward u32 and not a struct is to hopefully make ABIs easier later. /// Of course you could just translate on the ABI boundary, but my hope is that this /// design lets me realize some restrictions early on that I can't foresee yet. #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct InputKey(u32); impl InputKey { pub(crate) const fn new(v: u32) -> Self { Self(v) } pub(crate) const fn from_ascii(ch: char) -> Option<Self> { if ch == ' ' || (ch >= '0' && ch <= '9') { Some(Self(ch as u32)) } else if ch >= 'a' && ch <= 'z' { Some(Self(ch as u32 & !0x20)) // Shift a-z to A-Z } else if ch >= 'A' && ch <= 'Z' { Some(Self(kbmod::SHIFT.0 | ch as u32)) } else { None } } pub(crate) const fn value(&self) -> u32 { self.0 } pub(crate) const fn key(&self) -> Self { Self(self.0 & 0x00FFFFFF) } pub(crate) const fn modifiers(&self) -> InputKeyMod { InputKeyMod(self.0 & 0xFF000000) } pub(crate) const fn modifiers_contains(&self, modifier: InputKeyMod) -> bool { (self.0 & modifier.0) != 0 } pub(crate) const fn with_modifiers(&self, modifiers: InputKeyMod) -> Self { Self(self.0 | modifiers.0) } } /// A keyboard modifier. Ctrl/Alt/Shift. #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct InputKeyMod(u32); impl InputKeyMod { const fn new(v: u32) -> Self { Self(v) } pub(crate) const fn contains(&self, modifier: Self) -> bool { (self.0 & modifier.0) != 0 } } impl std::ops::BitOr<InputKeyMod> for InputKey { type Output = Self; fn bitor(self, rhs: InputKeyMod) -> Self { Self(self.0 | rhs.0) } } impl std::ops::BitOr<InputKey> for InputKeyMod { type Output = InputKey; fn bitor(self, rhs: InputKey) -> InputKey { InputKey(self.0 | rhs.0) } } impl std::ops::BitOrAssign for InputKeyMod { fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; } } /// Keyboard keys. /// /// The codes defined here match the VK_* constants on Windows. /// It's a convenient way to handle keyboard input, even on other platforms. pub mod vk { use super::InputKey; pub const NULL: InputKey = InputKey::new('\0' as u32); pub const BACK: InputKey = InputKey::new(0x08); pub const TAB: InputKey = InputKey::new('\t' as u32); pub const RETURN: InputKey = InputKey::new('\r' as u32); pub const ESCAPE: InputKey = InputKey::new(0x1B); pub const SPACE: InputKey = InputKey::new(' ' as u32); pub const PRIOR: InputKey = InputKey::new(0x21); pub const NEXT: InputKey = InputKey::new(0x22); pub const END: InputKey = InputKey::new(0x23); pub const HOME: InputKey = InputKey::new(0x24); pub const LEFT: InputKey = InputKey::new(0x25); pub const UP: InputKey = InputKey::new(0x26); pub const RIGHT: InputKey = InputKey::new(0x27); pub const DOWN: InputKey = InputKey::new(0x28); pub const INSERT: InputKey = InputKey::new(0x2D); pub const DELETE: InputKey = InputKey::new(0x2E); pub const N0: InputKey = InputKey::new('0' as u32); pub const N1: InputKey = InputKey::new('1' as u32); pub const N2: InputKey = InputKey::new('2' as u32); pub const N3: InputKey = InputKey::new('3' as u32); pub const N4: InputKey = InputKey::new('4' as u32); pub const N5: InputKey = InputKey::new('5' as u32); pub const N6: InputKey = InputKey::new('6' as u32); pub const N7: InputKey = InputKey::new('7' as u32); pub const N8: InputKey = InputKey::new('8' as u32); pub const N9: InputKey = InputKey::new('9' as u32); pub const A: InputKey = InputKey::new('A' as u32); pub const B: InputKey = InputKey::new('B' as u32); pub const C: InputKey = InputKey::new('C' as u32); pub const D: InputKey = InputKey::new('D' as u32); pub const E: InputKey = InputKey::new('E' as u32); pub const F: InputKey = InputKey::new('F' as u32); pub const G: InputKey = InputKey::new('G' as u32); pub const H: InputKey = InputKey::new('H' as u32); pub const I: InputKey = InputKey::new('I' as u32); pub const J: InputKey = InputKey::new('J' as u32); pub const K: InputKey = InputKey::new('K' as u32); pub const L: InputKey = InputKey::new('L' as u32); pub const M: InputKey = InputKey::new('M' as u32); pub const N: InputKey = InputKey::new('N' as u32); pub const O: InputKey = InputKey::new('O' as u32); pub const P: InputKey = InputKey::new('P' as u32); pub const Q: InputKey = InputKey::new('Q' as u32); pub const R: InputKey = InputKey::new('R' as u32); pub const S: InputKey = InputKey::new('S' as u32); pub const T: InputKey = InputKey::new('T' as u32); pub const U: InputKey = InputKey::new('U' as u32); pub const V: InputKey = InputKey::new('V' as u32); pub const W: InputKey = InputKey::new('W' as u32); pub const X: InputKey = InputKey::new('X' as u32); pub const Y: InputKey = InputKey::new('Y' as u32); pub const Z: InputKey = InputKey::new('Z' as u32); pub const NUMPAD0: InputKey = InputKey::new(0x60); pub const NUMPAD1: InputKey = InputKey::new(0x61); pub const NUMPAD2: InputKey = InputKey::new(0x62); pub const NUMPAD3: InputKey = InputKey::new(0x63); pub const NUMPAD4: InputKey = InputKey::new(0x64); pub const NUMPAD5: InputKey = InputKey::new(0x65); pub const NUMPAD6: InputKey = InputKey::new(0x66); pub const NUMPAD7: InputKey = InputKey::new(0x67); pub const NUMPAD8: InputKey = InputKey::new(0x68); pub const NUMPAD9: InputKey = InputKey::new(0x69); pub const MULTIPLY: InputKey = InputKey::new(0x6A); pub const ADD: InputKey = InputKey::new(0x6B); pub const SEPARATOR: InputKey = InputKey::new(0x6C); pub const SUBTRACT: InputKey = InputKey::new(0x6D); pub const DECIMAL: InputKey = InputKey::new(0x6E); pub const DIVIDE: InputKey = InputKey::new(0x6F); pub const F1: InputKey = InputKey::new(0x70); pub const F2: InputKey = InputKey::new(0x71); pub const F3: InputKey = InputKey::new(0x72); pub const F4: InputKey = InputKey::new(0x73); pub const F5: InputKey = InputKey::new(0x74); pub const F6: InputKey = InputKey::new(0x75); pub const F7: InputKey = InputKey::new(0x76); pub const F8: InputKey = InputKey::new(0x77); pub const F9: InputKey = InputKey::new(0x78); pub const F10: InputKey = InputKey::new(0x79); pub const F11: InputKey = InputKey::new(0x7A); pub const F12: InputKey = InputKey::new(0x7B); pub const F13: InputKey = InputKey::new(0x7C); pub const F14: InputKey = InputKey::new(0x7D); pub const F15: InputKey = InputKey::new(0x7E); pub const F16: InputKey = InputKey::new(0x7F); pub const F17: InputKey = InputKey::new(0x80); pub const F18: InputKey = InputKey::new(0x81); pub const F19: InputKey = InputKey::new(0x82); pub const F20: InputKey = InputKey::new(0x83); pub const F21: InputKey = InputKey::new(0x84); pub const F22: InputKey = InputKey::new(0x85); pub const F23: InputKey = InputKey::new(0x86); pub const F24: InputKey = InputKey::new(0x87); } /// Keyboard modifiers. pub mod kbmod { use super::InputKeyMod; pub const NONE: InputKeyMod = InputKeyMod::new(0x00000000); pub const CTRL: InputKeyMod = InputKeyMod::new(0x01000000); pub const ALT: InputKeyMod = InputKeyMod::new(0x02000000); pub const SHIFT: InputKeyMod = InputKeyMod::new(0x04000000); pub const CTRL_ALT: InputKeyMod = InputKeyMod::new(0x03000000); pub const CTRL_SHIFT: InputKeyMod = InputKeyMod::new(0x05000000); pub const ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x06000000); pub const CTRL_ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x07000000); } /// Text input. /// /// "Keyboard" input is also "text" input and vice versa. /// It differs in that text input can also be Unicode. #[derive(Clone, Copy)] pub struct InputText<'a> { pub text: &'a str, pub bracketed: bool, } /// Mouse input state. Up/Down, Left/Right, etc. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum InputMouseState { #[default] None, // These 3 carry their state between frames. Left, Middle, Right, // These 2 get reset to None on the next frame. Release, Scroll, } /// Mouse input. #[derive(Clone, Copy)] pub struct InputMouse { /// The state of the mouse.Up/Down, Left/Right, etc. pub state: InputMouseState, /// Any keyboard modifiers that are held down. pub modifiers: InputKeyMod, /// Position of the mouse in the viewport. pub position: Point, /// Scroll delta. pub scroll: Point, } /// Primary result type of the parser. pub enum Input<'input> { /// Window resize event. Resize(Size), /// Text input. /// /// Note that [`Input::Keyboard`] events can also be text. Text(InputText<'input>), /// Keyboard input. Keyboard(InputKey), /// Mouse input. Mouse(InputMouse), } /// Parses VT sequences into input events. pub struct Parser { bracketed_paste: bool, x10_mouse_want: bool, x10_mouse_buf: [u8; 3], x10_mouse_len: usize, } impl Parser { /// Creates a new parser that turns VT sequences into input events. /// /// Keep the instance alive for the lifetime of the input stream. pub fn new() -> Self { Self { bracketed_paste: false, x10_mouse_want: false, x10_mouse_buf: [0; 3], x10_mouse_len: 0, } } /// Takes an [`vt::Stream`] and returns a [`Stream`] /// that turns VT sequences into input events. pub fn parse<'parser, 'vt, 'input>( &'parser mut self, stream: vt::Stream<'vt, 'input>, ) -> Stream<'parser, 'vt, 'input> { Stream { parser: self, stream } } } /// An iterator that parses VT sequences into input events. pub struct Stream<'parser, 'vt, 'input> { parser: &'parser mut Parser, stream: vt::Stream<'vt, 'input>, } impl<'input> Iterator for Stream<'_, '_, 'input> { type Item = Input<'input>; fn next(&mut self) -> Option<Input<'input>> { loop { if self.parser.bracketed_paste { return self.handle_bracketed_paste(); } if self.parser.x10_mouse_want { return self.parse_x10_mouse_coordinates(); } const KEYPAD_LUT: [u8; 8] = [ vk::UP.value() as u8, // A vk::DOWN.value() as u8, // B vk::RIGHT.value() as u8, // C vk::LEFT.value() as u8, // D 0, // E vk::END.value() as u8, // F 0, // G vk::HOME.value() as u8, // H ]; match self.stream.next()? { vt::Token::Text(text) => { return Some(Input::Text(InputText { text, bracketed: false })); } vt::Token::Ctrl(ch) => match ch { '\0' | '\t' | '\r' => return Some(Input::Keyboard(InputKey::new(ch as u32))), '\n' => return Some(Input::Keyboard(kbmod::CTRL | vk::RETURN)), ..='\x1a' => { // Shift control code to A-Z let key = ch as u32 | 0x40; return Some(Input::Keyboard(kbmod::CTRL | InputKey::new(key))); } '\x7f' => return Some(Input::Keyboard(vk::BACK)), _ => {} }, vt::Token::Esc(ch) => { match ch { '\0' => return Some(Input::Keyboard(vk::ESCAPE)), '\n' => return Some(Input::Keyboard(kbmod::CTRL_ALT | vk::RETURN)), ' '..='~' => { let ch = ch as u32; let key = ch & !0x20; // Shift a-z to A-Z let modifiers = if (ch & 0x20) != 0 { kbmod::ALT } else { kbmod::ALT_SHIFT }; return Some(Input::Keyboard(modifiers | InputKey::new(key))); } _ => {} } } vt::Token::SS3(ch) => match ch { 'A'..='H' => { let vk = KEYPAD_LUT[ch as usize - 'A' as usize]; if vk != 0 { return Some(Input::Keyboard(InputKey::new(vk as u32))); } } 'P'..='S' => { let key = vk::F1.value() + ch as u32 - 'P' as u32; return Some(Input::Keyboard(InputKey::new(key))); } _ => {} }, vt::Token::Csi(csi) => { match csi.final_byte { 'A'..='H' => { let vk = KEYPAD_LUT[csi.final_byte as usize - 'A' as usize]; if vk != 0 { return Some(Input::Keyboard( InputKey::new(vk as u32) | Self::parse_modifiers(csi), )); } } 'Z' => return Some(Input::Keyboard(kbmod::SHIFT | vk::TAB)), '~' => { const LUT: [u8; 35] = [ 0, vk::HOME.value() as u8, // 1 vk::INSERT.value() as u8, // 2 vk::DELETE.value() as u8, // 3 vk::END.value() as u8, // 4 vk::PRIOR.value() as u8, // 5 vk::NEXT.value() as u8, // 6 0, 0, 0, 0, 0, 0, 0, 0, vk::F5.value() as u8, // 15 0, vk::F6.value() as u8, // 17 vk::F7.value() as u8, // 18 vk::F8.value() as u8, // 19 vk::F9.value() as u8, // 20 vk::F10.value() as u8, // 21 0, vk::F11.value() as u8, // 23 vk::F12.value() as u8, // 24 vk::F13.value() as u8, // 25 vk::F14.value() as u8, // 26 0, vk::F15.value() as u8, // 28 vk::F16.value() as u8, // 29 0, vk::F17.value() as u8, // 31 vk::F18.value() as u8, // 32 vk::F19.value() as u8, // 33 vk::F20.value() as u8, // 34 ]; const LUT_LEN: u16 = LUT.len() as u16; match csi.params[0] { 0..LUT_LEN => { let vk = LUT[csi.params[0] as usize]; if vk != 0 { return Some(Input::Keyboard( InputKey::new(vk as u32) | Self::parse_modifiers(csi), )); } } 200 => self.parser.bracketed_paste = true, _ => {} } } 'm' | 'M' if csi.private_byte == '<' => { let btn = csi.params[0]; let mut mouse = InputMouse { state: InputMouseState::None, modifiers: kbmod::NONE, position: Default::default(), scroll: Default::default(), }; mouse.state = InputMouseState::None; if (btn & 0x40) != 0 { mouse.state = InputMouseState::Scroll; mouse.scroll.y += if (btn & 0x01) != 0 { 3 } else { -3 }; } else if csi.final_byte == 'M' { const STATES: [InputMouseState; 4] = [ InputMouseState::Left, InputMouseState::Middle, InputMouseState::Right, InputMouseState::None, ]; mouse.state = STATES[(btn as usize) & 0x03]; } mouse.modifiers = kbmod::NONE; mouse.modifiers |= if (btn & 0x04) != 0 { kbmod::SHIFT } else { kbmod::NONE }; mouse.modifiers |= if (btn & 0x08) != 0 { kbmod::ALT } else { kbmod::NONE }; mouse.modifiers |= if (btn & 0x10f) != 0 { kbmod::CTRL } else { kbmod::NONE }; mouse.position.x = csi.params[1] as CoordType - 1; mouse.position.y = csi.params[2] as CoordType - 1; return Some(Input::Mouse(mouse)); } 'M' if csi.param_count == 0 => { self.parser.x10_mouse_want = true; } 't' if csi.params[0] == 8 => { // Window Size let width = (csi.params[2] as CoordType).clamp(1, 32767); let height = (csi.params[1] as CoordType).clamp(1, 32767); return Some(Input::Resize(Size { width, height })); } _ => {} } } _ => {} } } } } impl<'input> Stream<'_, '_, 'input> { /// Once we encounter the start of a bracketed paste /// we seek to the end of the paste in this function. /// /// A bracketed paste is basically: /// ```text /// <ESC>[201~ lots of text <ESC>[201~ /// ``` /// /// That in between text is then expected to be taken literally. /// It can be in between anything though, including other escape sequences. /// This is the reason why this is a separate method. #[cold] fn handle_bracketed_paste(&mut self) -> Option<Input<'input>> { let beg = self.stream.offset(); let mut end = beg; while let Some(token) = self.stream.next() { if let vt::Token::Csi(csi) = token && csi.final_byte == '~' && csi.params[0] == 201 { self.parser.bracketed_paste = false; break; } end = self.stream.offset(); } if end != beg { let input = self.stream.input(); Some(Input::Text(InputText { text: &input[beg..end], bracketed: true })) } else { None } } /// Implements the X10 mouse protocol via `CSI M CbCxCy`. /// /// You want to send numeric mouse coordinates. /// You have CSI sequences with numeric parameters. /// So, of course you put the coordinates as shifted ASCII characters after /// the end of the sequence. Limited coordinate range and complicated parsing! /// This is so puzzling to me. The existence of this function makes me unhappy. #[cold] fn parse_x10_mouse_coordinates(&mut self) -> Option<Input<'input>> { self.parser.x10_mouse_len += self.stream.read(&mut self.parser.x10_mouse_buf[self.parser.x10_mouse_len..]); if self.parser.x10_mouse_len < 3 { return None; } let button = self.parser.x10_mouse_buf[0] & 0b11; let modifier = self.parser.x10_mouse_buf[0] & 0b11100; let x = self.parser.x10_mouse_buf[1] as CoordType - 0x21; let y = self.parser.x10_mouse_buf[2] as CoordType - 0x21; let action = match button { 0 => InputMouseState::Left, 1 => InputMouseState::Middle, 2 => InputMouseState::Right, _ => InputMouseState::None, }; let modifiers = match modifier { 4 => kbmod::SHIFT, 8 => kbmod::ALT, 16 => kbmod::CTRL, _ => kbmod::NONE, }; self.parser.x10_mouse_want = false; self.parser.x10_mouse_len = 0; Some(Input::Mouse(InputMouse { state: action, modifiers, position: Point { x, y }, scroll: Default::default(), })) } fn parse_modifiers(csi: &vt::Csi) -> InputKeyMod { let mut modifiers = kbmod::NONE; let p1 = csi.params[1].saturating_sub(1); if (p1 & 0x01) != 0 { modifiers |= kbmod::SHIFT; } if (p1 & 0x02) != 0 { modifiers |= kbmod::ALT; } if (p1 & 0x04) != 0 { modifiers |= kbmod::CTRL; } modifiers } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mixelpixx/microsoft-edit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server