use crate::message::{Message, Params, Parse, parse_with_limit};
use std::fmt;
use std::ops::{BitOr, BitOrAssign};
pub const ENABLE: &[u8] = b"\x1bP=Fzks;on=1,v=0\x1b\\";
pub const DISABLE: &[u8] = b"\x1bP=Fzks;on=0\x1b\\";
pub const ENABLE_STR: &str = "\x1bP=Fzks;on=1,v=0\x1b\\";
pub const DISABLE_STR: &str = "\x1bP=Fzks;on=0\x1b\\";
pub fn set_mode(on: bool, id: Option<u32>) -> Vec<u8> {
let mut p = Params::new().with("on", u8::from(on));
if on {
p.push("v", crate::VERSION);
}
if let Some(id) = id {
p.push("id", id);
}
Message::new(b'k', b's', p).encode()
}
pub fn query(id: u32) -> Vec<u8> {
Message::new(b'k', b'q', Params::new().with("id", id)).encode()
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ModeReply {
pub id: u32,
pub ok: bool,
pub on: bool,
}
impl ModeReply {
pub fn from_message(m: &Message) -> Option<Self> {
m.is(b'k', b'r').then(|| ModeReply {
id: m.params.num("id").unwrap_or(0),
ok: m.params.get("ok") == Some("1"),
on: m.params.get("on") == Some("1"),
})
}
pub fn encode(&self) -> Vec<u8> {
let p = Params::new().with("id", self.id).with("ok", u8::from(self.ok)).with("on", u8::from(self.on));
Message::new(b'k', b'r', p).encode()
}
pub fn new(id: u32, ok: bool, on: bool) -> Self {
ModeReply { id, ok, on }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum KeyKind {
#[default]
Press,
Repeat,
Release,
}
impl KeyKind {
fn letter(self) -> char {
match self {
KeyKind::Press => 'p',
KeyKind::Repeat => 'r',
KeyKind::Release => 'u',
}
}
}
macro_rules! bits {
($(#[$m:meta])* $name:ident { $($(#[$cm:meta])* $c:ident = $v:expr;)* }) => {
$(#[$m])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $name(u8);
impl $name {
pub const NONE: Self = Self(0);
$($(#[$cm])* pub const $c: Self = Self($v);)*
pub const fn from_bits(b: u8) -> Self { Self(b) }
pub const fn bits(self) -> u8 { self.0 }
pub const fn contains(self, o: Self) -> bool { self.0 & o.0 == o.0 }
pub const fn is_empty(self) -> bool { self.0 == 0 }
pub fn insert(&mut self, o: Self) { self.0 |= o.0 }
pub fn remove(&mut self, o: Self) { self.0 &= !o.0 }
}
impl BitOr for $name { type Output = Self; fn bitor(self, o: Self) -> Self { Self(self.0 | o.0) } }
impl BitOrAssign for $name { fn bitor_assign(&mut self, o: Self) { self.0 |= o.0 } }
};
}
bits!(
Modifiers {
SHIFT = 1;
ALT = 2;
CTRL = 4;
SUPER = 8;
HYPER = 16;
META = 32;
ALT_GR = 64;
}
);
bits!(
Locks {
CAPS_LOCK = 1;
NUM_LOCK = 2;
SCROLL_LOCK = 4;
}
);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ImePhase {
Start,
Update,
Commit,
Cancel,
}
impl ImePhase {
fn as_str(self) -> &'static str {
match self {
ImePhase::Start => "start",
ImePhase::Update => "update",
ImePhase::Commit => "commit",
ImePhase::Cancel => "cancel",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Key {
Char(char),
Named(String),
}
impl Key {
pub fn parse(v: &str) -> Key {
if let Some(h) = v.strip_prefix("U+") {
if !h.is_empty() && h.len() <= 6 && h.bytes().all(|b| b.is_ascii_hexdigit()) {
if let Some(c) = u32::from_str_radix(h, 16).ok().and_then(char::from_u32) {
return Key::Char(c);
}
}
}
Key::Named(v.to_string())
}
pub fn name(&self) -> Option<&str> {
match self {
Key::Named(n) => Some(n),
Key::Char(_) => None,
}
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Key::Char(c) => write!(f, "U+{:04X}", *c as u32),
Key::Named(n) => f.write_str(n),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub struct KeyEvent {
pub kind: KeyKind,
pub code: Option<String>,
pub key: Option<Key>,
pub mods: Modifiers,
pub locks: Locks,
pub text: Option<String>,
pub ime: Option<ImePhase>,
}
impl KeyEvent {
pub fn new(kind: KeyKind, code: impl Into<String>, key: Key) -> Self {
KeyEvent { kind, code: Some(code.into()), key: Some(key), ..Default::default() }
}
pub fn ime(phase: ImePhase, text: Option<String>) -> Self {
KeyEvent { ime: Some(phase), text, ..Default::default() }
}
pub fn with_mods(mut self, mods: Modifiers) -> Self {
self.mods = mods;
self
}
pub fn with_locks(mut self, locks: Locks) -> Self {
self.locks = locks;
self
}
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.text = Some(text.into());
self
}
pub fn from_message(m: &Message) -> Option<Self> {
if !m.is(b'k', b'e') {
return None;
}
let mut ev = KeyEvent::default();
let mut have_t = false;
for (k, v) in m.params.iter() {
match k {
"t" => {
have_t = true;
ev.kind = match v {
"p" => KeyKind::Press,
"r" => KeyKind::Repeat,
"u" => KeyKind::Release,
_ => return None,
}
}
"code" => ev.code = Some(v.to_string()).filter(|s| !s.is_empty()),
"key" => ev.key = (!v.is_empty()).then(|| Key::parse(v)),
"mods" => ev.mods = Modifiers(v.parse().ok()?),
"locks" => ev.locks = Locks(v.parse().ok()?),
"text" => ev.text = Some(hex_decode_utf8(v)?),
"ime" => {
ev.ime = Some(match v {
"start" => ImePhase::Start,
"update" => ImePhase::Update,
"commit" => ImePhase::Commit,
"cancel" => ImePhase::Cancel,
_ => return None,
})
}
_ => {}
}
}
if !have_t || (ev.ime.is_none() && ev.key.is_none()) {
return None;
}
Some(ev)
}
pub fn parse(bytes: &[u8]) -> Option<Self> {
Self::from_message(&Message::from_bytes(bytes)?)
}
pub fn to_message(&self) -> Message {
let mut p = Params::new().with("t", self.kind.letter());
if let Some(c) = &self.code {
p.push("code", c);
}
if let Some(k) = &self.key {
p.push("key", k);
}
if !self.mods.is_empty() {
p.push("mods", self.mods.0);
}
if !self.locks.is_empty() {
p.push("locks", self.locks.0);
}
if let Some(i) = self.ime {
p.push("ime", i.as_str());
}
if self.kind != KeyKind::Release {
if let Some(t) = self.text.as_deref().filter(|t| !t.is_empty()) {
p.push("text", hex_encode(t.as_bytes()));
}
}
Message::new(b'k', b'e', p)
}
pub fn encode(&self) -> Vec<u8> {
self.to_message().encode()
}
}
pub fn parse_event(buf: &[u8], max: usize) -> Parse<KeyEvent> {
parse_with_limit(buf, max).map(|m| KeyEvent::from_message(&m))
}
fn hex_encode(b: &[u8]) -> String {
let mut s = String::with_capacity(b.len() * 2);
for x in b {
s.push_str(&format!("{x:02x}"));
}
s
}
fn hex_decode_utf8(v: &str) -> Option<String> {
if v.len() % 2 != 0 || !v.is_ascii() {
return None;
}
let bytes = (0..v.len()).step_by(2).map(|i| u8::from_str_radix(&v[i..i + 2], 16).ok()).collect::<Option<Vec<u8>>>()?;
String::from_utf8(bytes).ok()
}