use std::collections::VecDeque;
use crossterm::event::{self, KeyCode, KeyModifiers, ModifierKeyCode};
use crate::{
Event,
pos::{Pos, Size},
};
use super::CtEvent;
#[derive(Debug, Default)]
pub struct InputQueue {
evs: VecDeque<Event>,
mods: ModChanges,
pub new_sz: Option<Size>,
}
macro_rules! mod_changes {
($(
$field:ident: $mkc:ident $(, $km:ident)?
);* $(;)?) => {
#[derive(Debug, Default)]
struct ModChanges { $( $field: Option<bool> ),* }
impl ModChanges {} impl ModChanges {
fn push_mkc(&mut self, down: bool, mkc: ModifierKeyCode) {
match mkc {
$(
ModifierKeyCode::$mkc => self.$field = Some(down),
)*
other => todo!("support modifier {other}"),
}
}
fn push_mods(&mut self, down: bool, mods: KeyModifiers) { $($(
if mods.contains(KeyModifiers::$km) {
self.$field = Some(down);
}
)?)* }
fn pop(&mut self, evs: &mut VecDeque<Event>) { $(
if let Some(down) = self.$field.take() {
if down {
evs.push_back(Event::KeyPress(KeyCode::Modifier(ModifierKeyCode::$mkc)))
} else {
evs.push_back(Event::KeyRelease(KeyCode::Modifier(ModifierKeyCode::$mkc)))
}
}
)* }
}
}
}
mod_changes! {
lctrl: LeftControl, CONTROL; rctrl: RightControl;
lalt: LeftAlt, ALT; ralt: RightAlt;
lwin: LeftSuper, SUPER; rwin: RightSuper;
lshift: LeftShift, SHIFT; rshift: RightShift;
lhyper: LeftHyper, HYPER; rhyper: RightHyper;
lmeta: LeftMeta, META; rmeta: RightMeta;
}
impl InputQueue {
pub fn push_ct(&mut self, ct: CtEvent) {
match ct {
CtEvent::FocusGained | CtEvent::FocusLost => self.push(Event::Redraw),
CtEvent::Paste(p) => self.push(Event::Paste(p.into())),
CtEvent::Resize(w, h) => self.new_sz = Some(Size::rnew(w as _, h as _)),
CtEvent::Key(ke) => {
self.mods.push_mods(true, ke.modifiers);
match ke.kind {
event::KeyEventKind::Press => self.push(Event::KeyPress(ke.code)),
event::KeyEventKind::Repeat => self.push_all([
Event::KeyRelease(ke.code),
Event::KeyPress(ke.code),
]),
event::KeyEventKind::Release => self.push(Event::KeyRelease(ke.code)),
}
self.mods.push_mods(false, ke.modifiers);
}
CtEvent::Mouse(me) => self.push(Event::MouseMove(Pos::rnew(me.column as _, me.row as _))),
}
}
fn push(&mut self, ev: Event) {
match ev {
Event::KeyPress(KeyCode::Modifier(mkc)) => {
self.mods.push_mkc(true, mkc);
return;
}
Event::KeyRelease(KeyCode::Modifier(mkc)) => {
self.mods.push_mkc(false, mkc);
return;
}
_ => self.mods.pop(&mut self.evs),
}
if let Event::MouseMove(np) = ev
&& let Some(Event::MouseMove(op)) = self.evs.back_mut()
{
*op = np;
return;
}
self.evs.push_back(ev);
}
#[inline]
fn push_all(&mut self, evs: impl IntoIterator<Item = Event>) {
for ev in evs {
self.push(ev);
}
}
#[inline]
pub fn pop(&mut self) -> Option<Event> {
if self.evs.is_empty() {
self.mods.pop(&mut self.evs);
}
self.evs.pop_front()
}
}