use crate::client::WMClient;
use crate::config::expmap_operator::ExpmapOperator;
use crate::config::Expmap;
use crate::emit_handler::{Emit, EmitHandler};
use crate::event::Event;
use crate::event_handler::PRESS;
use crate::operator_double_tap::DoubleTapOperator;
use crate::operator_sim::SimOperator;
use crate::operators::{ActiveOperator, OperatorAction, OperatorEntry, StaticOperator};
use crate::timeout_manager::TimeoutManager;
use evdev::KeyCode as Key;
use std::collections::HashMap;
use std::rc::Rc;
use std::usize;
pub struct OperatorHandler {
active: Vec<Box<dyn ActiveOperator>>,
candidates: Option<Candidates>,
lookup_map: HashMap<Key, Vec<OperatorEntry>>,
emit_handler: EmitHandler,
}
impl OperatorHandler {
pub fn new(experimental_map: &Vec<Expmap>, timeout_manager: Rc<TimeoutManager>) -> OperatorHandler {
let mut lookup_map: HashMap<Key, Vec<OperatorEntry>> = HashMap::new();
for expmap in experimental_map {
for chord in &expmap.chords {
let operators = SimOperator {
keys: chord.keys.clone(),
actions: chord.actions.clone(),
timeout: chord.timeout,
timeout_manager: timeout_manager.clone(),
}
.get_operators();
for (key, operator) in operators {
let entry = OperatorEntry {
operator,
application: expmap.application.clone(),
title: expmap.window.clone(),
};
match lookup_map.get_mut(&key) {
Some(current) => {
current.push(entry);
}
None => {
lookup_map.insert(key, vec![entry]);
}
};
}
}
for (key, op) in &expmap.remap {
let operators = match op {
ExpmapOperator::DoubleTap(dbltap) => DoubleTapOperator {
key: key.clone(),
actions: dbltap.actions.clone(),
timeout: dbltap.timeout,
timeout_manager: timeout_manager.clone(),
},
}
.get_operators();
for (key, operator) in operators {
let entry = OperatorEntry {
operator,
application: expmap.application.clone(),
title: expmap.window.clone(),
};
match lookup_map.get_mut(&key) {
Some(current) => {
current.push(entry);
}
None => {
lookup_map.insert(key, vec![entry]);
}
};
}
}
}
OperatorHandler {
active: vec![],
candidates: None,
lookup_map,
emit_handler: EmitHandler::new(),
}
}
#[cfg(test)]
pub fn assert_base_state(&self) {
assert!(self.active.is_empty());
assert!(self.candidates.is_none());
}
#[cfg(test)]
pub fn assert_emitted_modifiers_are_synced(&self) {
self.emit_handler.assert_emitted_modifiers_are_synced();
}
#[cfg(test)]
pub fn map_evs(&mut self, events: Vec<Event>) -> Vec<Event> {
let mut wmclient = WMClient::new("none", Box::new(crate::client::null_client::NullClient), false);
self.map_events(events, &mut wmclient)
}
pub fn map_events(&mut self, events: Vec<Event>, wmclient: &mut WMClient) -> Vec<Event> {
events
.into_iter()
.flat_map(|event| {
self.emit_handler.on_event(&event);
let events = process_event(event, &mut self.active, &mut self.candidates, &self.lookup_map, wmclient);
self.emit_handler.map_output(events)
})
.collect()
}
}
#[derive(Debug)]
enum CandidateState {
Canceled,
Done,
Matching,
}
#[derive(Debug)]
struct Candidate {
operator: Box<dyn ActiveOperator>,
state: CandidateState,
emitted: Vec<Emit>,
unhandled: Vec<Event>,
}
#[derive(Debug)]
struct Candidates {
start_event: Event,
events: Vec<Event>,
operators: Vec<Candidate>,
}
enum Node {
Event(Event),
Operator(Box<dyn ActiveOperator>),
CandidateChosen(usize),
CandidatesCanceled,
}
fn process_event(
event: Event,
right: &mut Vec<Box<dyn ActiveOperator>>,
candidates: &mut Option<Candidates>,
lookup_map: &HashMap<Key, Vec<OperatorEntry>>,
wmclient: &mut WMClient,
) -> Vec<Emit> {
let mut emit: Vec<Emit> = vec![];
let mut left: Vec<Node> = vec![Node::Event(event)];
loop {
match left.pop() {
Some(Node::Event(event)) => {
match right.pop() {
Some(mut operator) => match operator.on_event(&event) {
OperatorAction::Undecided => {
right.push(operator);
}
OperatorAction::Cancel => {
unreachable!()
}
OperatorAction::Unhandled => {
left.push(Node::Operator(operator));
left.push(Node::Event(event));
}
OperatorAction::Partial(emitted, unhandled) => {
right.push(operator);
emit.extend(emitted);
unhandled_back_to_stack(unhandled, &mut left);
}
OperatorAction::Done(new_emit, unhandled) => {
emit.extend(new_emit);
unhandled_back_to_stack(unhandled, &mut left);
}
},
None => {
match candidates {
Some(candidates) => try_candidates(event, &mut left, candidates),
None => static_lookup(event, candidates, &lookup_map, &mut emit, wmclient),
};
}
};
}
Some(Node::Operator(operator)) => {
right.push(operator);
}
Some(Node::CandidateChosen(chosen)) => {
let candidate = candidates.take().unwrap().operators.into_iter().nth(chosen).unwrap();
emit.extend(candidate.emitted);
if !matches!(candidate.state, CandidateState::Done) {
right.push(candidate.operator);
}
unhandled_back_to_stack(candidate.unhandled, &mut left);
}
Some(Node::CandidatesCanceled) => {
let taken = candidates.take().unwrap();
emit.push(Emit::Single(taken.start_event.clone()));
unhandled_back_to_stack(taken.events, &mut left);
}
None => {
return emit;
}
};
}
}
fn unhandled_back_to_stack(events: Vec<Event>, nodes: &mut Vec<Node>) {
nodes.extend(
events
.iter()
.rev()
.map(|event| Node::Event(event.clone()))
.collect::<Vec<_>>(),
);
}
fn try_candidates(event: Event, left: &mut Vec<Node>, candidates: &mut Candidates) {
candidates.events.push(event.clone());
let mut first = true;
for (usize, candidate) in candidates.operators.iter_mut().enumerate() {
match candidate.state {
CandidateState::Canceled => {
continue;
}
CandidateState::Done => {
if first {
if !matches!(event, Event::Tick) {
todo!()
}
candidate.unhandled.push(event.clone());
left.push(Node::CandidateChosen(usize));
return;
} else {
candidate.unhandled.push(event.clone());
}
}
CandidateState::Matching => {
match candidate.operator.on_event(&event) {
OperatorAction::Undecided => {
first = false;
}
OperatorAction::Cancel => {
candidate.state = CandidateState::Canceled;
}
OperatorAction::Unhandled => {
candidate.unhandled.push(event.clone());
if first {
left.push(Node::CandidateChosen(usize));
return;
}
}
OperatorAction::Partial(new_emit, unhandled) => {
candidate.emitted.extend(new_emit);
candidate.unhandled.extend(unhandled);
if first {
left.push(Node::CandidateChosen(usize));
return;
}
}
OperatorAction::Done(new_emit, unhandled) => {
candidate.emitted.extend(new_emit);
candidate.unhandled.extend(unhandled);
candidate.state = CandidateState::Done;
if first {
left.push(Node::CandidateChosen(usize));
return;
}
}
};
}
};
}
if first {
left.push(Node::CandidatesCanceled);
}
}
fn static_lookup(
event: Event,
candidates: &mut Option<Candidates>,
lookup_map: &HashMap<Key, Vec<OperatorEntry>>,
emit: &mut Vec<Emit>,
wmclient: &mut WMClient,
) {
let (device, key_event) = match &event {
Event::KeyEvent(device, key_event) => (device, key_event),
Event::Tick => {
return;
}
_ => {
emit.push(Emit::Single(event));
return;
}
};
if key_event.value() != PRESS {
emit.push(Emit::key_event(device.clone(), key_event.clone()));
return;
}
match lookup_map.get(&key_event.key) {
Some(entries) => {
debug_assert!(!entries.is_empty());
debug_assert!(candidates.is_none());
let new_candidates: Vec<_> = entries
.iter()
.filter(|entry| {
if let Some(window_matcher) = &entry.title {
if !wmclient.match_window(window_matcher) {
return false;
}
}
if let Some(application_matcher) = &entry.application {
if !wmclient.match_application(application_matcher) {
return false;
}
}
true
})
.map(|entry| Candidate {
operator: entry.operator.get_active_operator(&event),
state: CandidateState::Matching,
emitted: vec![],
unhandled: vec![],
})
.collect();
candidates.replace(Candidates {
start_event: event,
events: vec![],
operators: new_candidates,
});
}
None => {
emit.push(Emit::key_event(device.clone(), key_event.clone()));
}
};
}