1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-26 16:34:23 +03:00

send keyboard input to the pty

This commit is contained in:
Wez Furlong 2018-01-27 16:25:34 -08:00
parent f879447d59
commit 1ae85b2349
6 changed files with 388 additions and 5 deletions

View File

@ -4,6 +4,7 @@ name = "wterm"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
bitflags = "1.0.1"
failure = "0.1.1" failure = "0.1.1"
freetype = "0.3.0" freetype = "0.3.0"
harfbuzz-sys = "0.1.15" harfbuzz-sys = "0.1.15"
@ -23,5 +24,6 @@ features = [
"ewmh", "ewmh",
"misc", "misc",
"shm", "shm",
"keysyms",
] ]
version = "0.2.0" version = "0.2.0"

View File

@ -1,5 +1,7 @@
#[macro_use] #[macro_use]
extern crate failure; extern crate failure;
#[macro_use]
extern crate bitflags;
extern crate unicode_width; extern crate unicode_width;
extern crate unicode_segmentation; extern crate unicode_segmentation;
extern crate harfbuzz_sys; extern crate harfbuzz_sys;
@ -26,6 +28,7 @@ use std::process::Command;
use std::time::Duration; use std::time::Duration;
mod xgfx; mod xgfx;
mod xkeysyms;
mod font; mod font;
use font::{Font, FontPattern, ftwrap}; use font::{Font, FontPattern, ftwrap};
@ -57,7 +60,11 @@ fn dispatch_gui(
} }
xcb::KEY_PRESS => { xcb::KEY_PRESS => {
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(&event) }; let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(&event) };
println!("Key '{}' pressed", key_press.detail()); window.key_down(key_press)?;
}
xcb::KEY_RELEASE => {
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(&event) };
window.key_up(key_press)?;
} }
xcb::CLIENT_MESSAGE => { xcb::CLIENT_MESSAGE => {
let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(&event) }; let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(&event) };
@ -101,8 +108,7 @@ fn run() -> Result<(), Error> {
initial_pixel_height, initial_pixel_height,
)?; )?;
let mut cmd = Command::new("top"); let cmd = Command::new("zsh");
// cmd.arg("-l");
let child = slave.spawn_command(cmd)?; let child = slave.spawn_command(cmd)?;
eprintln!("spawned: {:?}", child); eprintln!("spawned: {:?}", child);

View File

@ -1,11 +1,35 @@
//! Terminal model //! Terminal model
use failure::Error;
use std; use std;
use unicode_segmentation; use unicode_segmentation;
use vte; use vte;
pub mod color; pub mod color;
bitflags! {
#[derive(Default)]
pub struct KeyModifiers :u8{
const CTRL = 1;
const ALT = 2;
const META = 4;
const SUPER = 8;
const SHIFT = 16;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum KeyCode {
Char(char),
Unknown,
Control,
Alt,
Meta,
Super,
Hyper,
Shift,
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct CellAttributes { pub struct CellAttributes {
attributes: u16, attributes: u16,
@ -328,6 +352,48 @@ impl TerminalState {
&mut self.screen &mut self.screen
} }
} }
pub fn key_down<W: std::io::Write>(
&mut self,
key: KeyCode,
mods: KeyModifiers,
write: &mut W,
) -> Result<(), Error> {
match key {
KeyCode::Char(c) => {
let adjusted = if mods.contains(KeyModifiers::CTRL) && c <= 0xff as char {
if mods.contains(KeyModifiers::SHIFT) {
// If shift is held we have C == 0x43 and want to translate
// that into 0x03
(c as u8 - 0x40) as char
} else {
// If shift is not held we have c == 0x63 and want to translate
// that into 0x03
(c as u8 - 0x60) as char
}
} else if mods.contains(KeyModifiers::ALT) && c <= 0xff as char {
(c as u8 | 0x80) as char
} else {
c
};
let mut buf = [0; 8];
let encoded = adjusted.encode_utf8(&mut buf);
write.write(encoded.as_bytes())?;
Ok(())
}
_ => Ok(()),
}
}
pub fn key_up<W: std::io::Write>(
&mut self,
key: KeyCode,
mods: KeyModifiers,
write: &mut W,
) -> Result<(), Error> {
Ok(())
}
} }
pub struct Terminal { pub struct Terminal {
@ -390,6 +456,24 @@ impl Terminal {
let len = screen.lines.len(); let len = screen.lines.len();
(width, &screen.lines[len - height..len]) (width, &screen.lines[len - height..len])
} }
pub fn key_down<W: std::io::Write>(
&mut self,
key: KeyCode,
mods: KeyModifiers,
write: &mut W,
) -> Result<(), Error> {
self.state.key_down(key, mods, write)
}
pub fn key_up<W: std::io::Write>(
&mut self,
key: KeyCode,
mods: KeyModifiers,
write: &mut W,
) -> Result<(), Error> {
self.state.key_up(key, mods, write)
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -4,9 +4,12 @@ use std::ops::Deref;
use std::result; use std::result;
use xcb; use xcb;
use xcb_util; use xcb_util;
use xcb_util::ffi::keysyms::{xcb_key_press_lookup_keysym, xcb_key_symbols_alloc,
xcb_key_symbols_free, xcb_key_symbols_t};
use failure::{self, Error}; use failure::{self, Error};
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
pub use xkeysyms::*;
use super::term::color::RgbColor; use super::term::color::RgbColor;
@ -15,6 +18,7 @@ pub struct Connection {
screen_num: i32, screen_num: i32,
atom_protocols: xcb::Atom, atom_protocols: xcb::Atom,
atom_delete: xcb::Atom, atom_delete: xcb::Atom,
keysyms: *mut xcb_key_symbols_t,
} }
impl Deref for Connection { impl Deref for Connection {
@ -35,11 +39,14 @@ impl Connection {
.get_reply()? .get_reply()?
.atom(); .atom();
let keysyms = unsafe { xcb_key_symbols_alloc(conn.get_raw_conn()) };
Ok(Connection { Ok(Connection {
conn, conn,
screen_num, screen_num,
atom_protocols, atom_protocols,
atom_delete, atom_delete,
keysyms,
}) })
} }
@ -54,6 +61,26 @@ impl Connection {
pub fn atom_delete(&self) -> xcb::Atom { pub fn atom_delete(&self) -> xcb::Atom {
self.atom_delete self.atom_delete
} }
pub fn lookup_keysym(&self, event: &xcb::KeyPressEvent, shifted: bool) -> xcb::Keysym {
unsafe {
let sym =
xcb_key_press_lookup_keysym(self.keysyms, event.ptr, if shifted { 1 } else { 0 });
if sym == 0 && shifted {
xcb_key_press_lookup_keysym(self.keysyms, event.ptr, 0)
} else {
sym
}
}
}
}
impl Drop for Connection {
fn drop(&mut self) {
unsafe {
xcb_key_symbols_free(self.keysyms);
}
}
} }
/// The X protocol allows referencing a number of drawable /// The X protocol allows referencing a number of drawable
@ -105,7 +132,9 @@ impl<'a> Window<'a> {
&[ &[
( (
xcb::CW_EVENT_MASK, xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS | xcb::EVENT_MASK_STRUCTURE_NOTIFY, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS |
xcb::EVENT_MASK_KEY_RELEASE |
xcb::EVENT_MASK_STRUCTURE_NOTIFY,
), ),
], ],
).request_check()?; ).request_check()?;

239
src/xkeysyms.rs Normal file
View File

@ -0,0 +1,239 @@
#![allow(non_upper_case_globals, dead_code)]
use xcb::KeyPressEvent;
use xcb::ffi::xproto::xcb_keysym_t;
pub const XK_VoidSymbol: xcb_keysym_t = 0xffffff;
pub const XK_BackSpace: xcb_keysym_t = 0xff08;
pub const XK_Tab: xcb_keysym_t = 0xff09;
pub const XK_Linefeed: xcb_keysym_t = 0xff0a;
pub const XK_Clear: xcb_keysym_t = 0xff0b;
pub const XK_Return: xcb_keysym_t = 0xff0d;
pub const XK_Pause: xcb_keysym_t = 0xff13;
pub const XK_Scroll_Lock: xcb_keysym_t = 0xff14;
pub const XK_Sys_Req: xcb_keysym_t = 0xff15;
pub const XK_Escape: xcb_keysym_t = 0xff1b;
pub const XK_Delete: xcb_keysym_t = 0xffff;
pub const XK_Home: xcb_keysym_t = 0xff50;
pub const XK_Left: xcb_keysym_t = 0xff51;
pub const XK_Up: xcb_keysym_t = 0xff52;
pub const XK_Right: xcb_keysym_t = 0xff53;
pub const XK_Down: xcb_keysym_t = 0xff54;
pub const XK_Prior: xcb_keysym_t = 0xff55;
pub const XK_Page_Up: xcb_keysym_t = 0xff55;
pub const XK_Next: xcb_keysym_t = 0xff56;
pub const XK_Page_Down: xcb_keysym_t = 0xff56;
pub const XK_End: xcb_keysym_t = 0xff57;
pub const XK_Begin: xcb_keysym_t = 0xff58;
pub const XK_KP_Space: xcb_keysym_t = 0xff80;
pub const XK_KP_Tab: xcb_keysym_t = 0xff89;
pub const XK_KP_Enter: xcb_keysym_t = 0xff8d;
pub const XK_KP_F1: xcb_keysym_t = 0xff91;
pub const XK_KP_F2: xcb_keysym_t = 0xff92;
pub const XK_KP_F3: xcb_keysym_t = 0xff93;
pub const XK_KP_F4: xcb_keysym_t = 0xff94;
pub const XK_KP_Home: xcb_keysym_t = 0xff95;
pub const XK_KP_Left: xcb_keysym_t = 0xff96;
pub const XK_KP_Up: xcb_keysym_t = 0xff97;
pub const XK_KP_Right: xcb_keysym_t = 0xff98;
pub const XK_KP_Down: xcb_keysym_t = 0xff99;
pub const XK_KP_Prior: xcb_keysym_t = 0xff9a;
pub const XK_KP_Page_Up: xcb_keysym_t = 0xff9a;
pub const XK_KP_Next: xcb_keysym_t = 0xff9b;
pub const XK_KP_Page_Down: xcb_keysym_t = 0xff9b;
pub const XK_KP_End: xcb_keysym_t = 0xff9c;
pub const XK_KP_Begin: xcb_keysym_t = 0xff9d;
pub const XK_KP_Insert: xcb_keysym_t = 0xff9e;
pub const XK_KP_Delete: xcb_keysym_t = 0xff9f;
pub const XK_KP_Equal: xcb_keysym_t = 0xffbd;
pub const XK_KP_Multiply: xcb_keysym_t = 0xffaa;
pub const XK_KP_Add: xcb_keysym_t = 0xffab;
pub const XK_KP_Separator: xcb_keysym_t = 0xffac;
pub const XK_KP_Subtract: xcb_keysym_t = 0xffad;
pub const XK_KP_Decimal: xcb_keysym_t = 0xffae;
pub const XK_KP_Divide: xcb_keysym_t = 0xffaf;
pub const XK_KP_0: xcb_keysym_t = 0xffb0;
pub const XK_KP_1: xcb_keysym_t = 0xffb1;
pub const XK_KP_2: xcb_keysym_t = 0xffb2;
pub const XK_KP_3: xcb_keysym_t = 0xffb3;
pub const XK_KP_4: xcb_keysym_t = 0xffb4;
pub const XK_KP_5: xcb_keysym_t = 0xffb5;
pub const XK_KP_6: xcb_keysym_t = 0xffb6;
pub const XK_KP_7: xcb_keysym_t = 0xffb7;
pub const XK_KP_8: xcb_keysym_t = 0xffb8;
pub const XK_KP_9: xcb_keysym_t = 0xffb9;
pub const XK_F1: xcb_keysym_t = 0xffbe;
pub const XK_F2: xcb_keysym_t = 0xffbf;
pub const XK_F3: xcb_keysym_t = 0xffc0;
pub const XK_F4: xcb_keysym_t = 0xffc1;
pub const XK_F5: xcb_keysym_t = 0xffc2;
pub const XK_F6: xcb_keysym_t = 0xffc3;
pub const XK_F7: xcb_keysym_t = 0xffc4;
pub const XK_F8: xcb_keysym_t = 0xffc5;
pub const XK_F9: xcb_keysym_t = 0xffc6;
pub const XK_F10: xcb_keysym_t = 0xffc7;
pub const XK_F11: xcb_keysym_t = 0xffc8;
pub const XK_F12: xcb_keysym_t = 0xffc9;
pub const XK_Shift_L: xcb_keysym_t = 0xffe1;
pub const XK_Shift_R: xcb_keysym_t = 0xffe2;
pub const XK_Control_L: xcb_keysym_t = 0xffe3;
pub const XK_Control_R: xcb_keysym_t = 0xffe4;
pub const XK_Caps_Lock: xcb_keysym_t = 0xffe5;
pub const XK_Shift_Lock: xcb_keysym_t = 0xffe6;
pub const XK_Meta_L: xcb_keysym_t = 0xffe7;
pub const XK_Meta_R: xcb_keysym_t = 0xffe8;
pub const XK_Alt_L: xcb_keysym_t = 0xffe9;
pub const XK_Alt_R: xcb_keysym_t = 0xffea;
pub const XK_Super_L: xcb_keysym_t = 0xffeb;
pub const XK_Super_R: xcb_keysym_t = 0xffec;
pub const XK_Hyper_L: xcb_keysym_t = 0xffed;
pub const XK_Hyper_R: xcb_keysym_t = 0xffee;
pub const XK_space: xcb_keysym_t = 0x0020;
pub const XK_exclam: xcb_keysym_t = 0x0021;
pub const XK_quotedbl: xcb_keysym_t = 0x0022;
pub const XK_numbersign: xcb_keysym_t = 0x0023;
pub const XK_dollar: xcb_keysym_t = 0x0024;
pub const XK_percent: xcb_keysym_t = 0x0025;
pub const XK_ampersand: xcb_keysym_t = 0x0026;
pub const XK_apostrophe: xcb_keysym_t = 0x0027;
pub const XK_quoteright: xcb_keysym_t = 0x0027;
pub const XK_parenleft: xcb_keysym_t = 0x0028;
pub const XK_parenright: xcb_keysym_t = 0x0029;
pub const XK_asterisk: xcb_keysym_t = 0x002a;
pub const XK_plus: xcb_keysym_t = 0x002b;
pub const XK_comma: xcb_keysym_t = 0x002c;
pub const XK_minus: xcb_keysym_t = 0x002d;
pub const XK_period: xcb_keysym_t = 0x002e;
pub const XK_slash: xcb_keysym_t = 0x002f;
pub const XK_0: xcb_keysym_t = 0x0030;
pub const XK_1: xcb_keysym_t = 0x0031;
pub const XK_2: xcb_keysym_t = 0x0032;
pub const XK_3: xcb_keysym_t = 0x0033;
pub const XK_4: xcb_keysym_t = 0x0034;
pub const XK_5: xcb_keysym_t = 0x0035;
pub const XK_6: xcb_keysym_t = 0x0036;
pub const XK_7: xcb_keysym_t = 0x0037;
pub const XK_8: xcb_keysym_t = 0x0038;
pub const XK_9: xcb_keysym_t = 0x0039;
pub const XK_colon: xcb_keysym_t = 0x003a;
pub const XK_semicolon: xcb_keysym_t = 0x003b;
pub const XK_less: xcb_keysym_t = 0x003c;
pub const XK_equal: xcb_keysym_t = 0x003d;
pub const XK_greater: xcb_keysym_t = 0x003e;
pub const XK_question: xcb_keysym_t = 0x003f;
pub const XK_at: xcb_keysym_t = 0x0040;
pub const XK_A: xcb_keysym_t = 0x0041;
pub const XK_B: xcb_keysym_t = 0x0042;
pub const XK_C: xcb_keysym_t = 0x0043;
pub const XK_D: xcb_keysym_t = 0x0044;
pub const XK_E: xcb_keysym_t = 0x0045;
pub const XK_F: xcb_keysym_t = 0x0046;
pub const XK_G: xcb_keysym_t = 0x0047;
pub const XK_H: xcb_keysym_t = 0x0048;
pub const XK_I: xcb_keysym_t = 0x0049;
pub const XK_J: xcb_keysym_t = 0x004a;
pub const XK_K: xcb_keysym_t = 0x004b;
pub const XK_L: xcb_keysym_t = 0x004c;
pub const XK_M: xcb_keysym_t = 0x004d;
pub const XK_N: xcb_keysym_t = 0x004e;
pub const XK_O: xcb_keysym_t = 0x004f;
pub const XK_P: xcb_keysym_t = 0x0050;
pub const XK_Q: xcb_keysym_t = 0x0051;
pub const XK_R: xcb_keysym_t = 0x0052;
pub const XK_S: xcb_keysym_t = 0x0053;
pub const XK_T: xcb_keysym_t = 0x0054;
pub const XK_U: xcb_keysym_t = 0x0055;
pub const XK_V: xcb_keysym_t = 0x0056;
pub const XK_W: xcb_keysym_t = 0x0057;
pub const XK_X: xcb_keysym_t = 0x0058;
pub const XK_Y: xcb_keysym_t = 0x0059;
pub const XK_Z: xcb_keysym_t = 0x005a;
pub const XK_bracketleft: xcb_keysym_t = 0x005b;
pub const XK_backslash: xcb_keysym_t = 0x005c;
pub const XK_bracketright: xcb_keysym_t = 0x005d;
pub const XK_asciicircum: xcb_keysym_t = 0x005e;
pub const XK_underscore: xcb_keysym_t = 0x005f;
pub const XK_grave: xcb_keysym_t = 0x0060;
pub const XK_quoteleft: xcb_keysym_t = 0x0060;
pub const XK_a: xcb_keysym_t = 0x0061;
pub const XK_b: xcb_keysym_t = 0x0062;
pub const XK_c: xcb_keysym_t = 0x0063;
pub const XK_d: xcb_keysym_t = 0x0064;
pub const XK_e: xcb_keysym_t = 0x0065;
pub const XK_f: xcb_keysym_t = 0x0066;
pub const XK_g: xcb_keysym_t = 0x0067;
pub const XK_h: xcb_keysym_t = 0x0068;
pub const XK_i: xcb_keysym_t = 0x0069;
pub const XK_j: xcb_keysym_t = 0x006a;
pub const XK_k: xcb_keysym_t = 0x006b;
pub const XK_l: xcb_keysym_t = 0x006c;
pub const XK_m: xcb_keysym_t = 0x006d;
pub const XK_n: xcb_keysym_t = 0x006e;
pub const XK_o: xcb_keysym_t = 0x006f;
pub const XK_p: xcb_keysym_t = 0x0070;
pub const XK_q: xcb_keysym_t = 0x0071;
pub const XK_r: xcb_keysym_t = 0x0072;
pub const XK_s: xcb_keysym_t = 0x0073;
pub const XK_t: xcb_keysym_t = 0x0074;
pub const XK_u: xcb_keysym_t = 0x0075;
pub const XK_v: xcb_keysym_t = 0x0076;
pub const XK_w: xcb_keysym_t = 0x0077;
pub const XK_x: xcb_keysym_t = 0x0078;
pub const XK_y: xcb_keysym_t = 0x0079;
pub const XK_z: xcb_keysym_t = 0x007a;
pub const XK_braceleft: xcb_keysym_t = 0x007b;
pub const XK_bar: xcb_keysym_t = 0x007c;
pub const XK_braceright: xcb_keysym_t = 0x007d;
pub const XK_asciitilde: xcb_keysym_t = 0x007e;
use term::KeyCode;
use term::KeyModifiers;
impl From<xcb_keysym_t> for KeyCode {
fn from(k: xcb_keysym_t) -> Self {
match k {
XK_space...XK_asciitilde => {
// This range overlaps with ascii
KeyCode::Char(k as u8 as char)
}
XK_BackSpace | XK_Tab | XK_Linefeed | XK_Return | XK_Escape => {
KeyCode::Char((k & 0xff) as u8 as char)
}
XK_Control_L | XK_Control_R => KeyCode::Control,
XK_Alt_L | XK_Alt_R => KeyCode::Alt,
XK_Meta_L | XK_Meta_R => KeyCode::Meta,
XK_Super_L | XK_Super_R => KeyCode::Super,
XK_Hyper_L | XK_Hyper_R => KeyCode::Hyper,
XK_Shift_L | XK_Shift_R => KeyCode::Shift,
_ => KeyCode::Unknown,
}
}
}
pub fn modifiers(event: &KeyPressEvent) -> KeyModifiers {
use xcb::xproto::*;
let mut mods = KeyModifiers::default();
let state = event.state() as u32;
if state & MOD_MASK_SHIFT != 0 {
mods |= KeyModifiers::SHIFT;
}
if state & MOD_MASK_CONTROL != 0 {
mods |= KeyModifiers::CTRL;
}
if state & MOD_MASK_1 != 0 {
mods |= KeyModifiers::ALT;
}
if state & MOD_MASK_4 != 0 {
mods |= KeyModifiers::SUPER;
}
mods
}

View File

@ -5,8 +5,10 @@ use std::io::Read;
use std::mem; use std::mem;
use std::process::Child; use std::process::Child;
use std::slice; use std::slice;
use term; use term::{self, KeyCode, KeyModifiers};
use xcb;
use xgfx::{self, Connection, Drawable}; use xgfx::{self, Connection, Drawable};
use xkeysyms;
pub struct TerminalWindow<'a> { pub struct TerminalWindow<'a> {
window: xgfx::Window<'a>, window: xgfx::Window<'a>,
@ -324,4 +326,25 @@ impl<'a> TerminalWindow<'a> {
pub fn need_paint(&self) -> bool { pub fn need_paint(&self) -> bool {
self.need_paint self.need_paint
} }
fn decode_key(&self, event: &xcb::KeyPressEvent) -> (KeyCode, KeyModifiers) {
let mods = xkeysyms::modifiers(event);
let sym = self.conn.lookup_keysym(
event,
mods.contains(KeyModifiers::SHIFT),
);
(sym.into(), mods)
}
pub fn key_down(&mut self, event: &xcb::KeyPressEvent) -> Result<(), Error> {
let (code, mods) = self.decode_key(event);
println!("Key pressed {:?} {:?}", code, mods);
self.terminal.key_down(code, mods, &mut self.pty)
}
pub fn key_up(&mut self, event: &xcb::KeyPressEvent) -> Result<(), Error> {
let (code, mods) = self.decode_key(event);
println!("Key released {:?} {:?}", code, mods);
self.terminal.key_up(code, mods, &mut self.pty)
}
} }