/* * This file is part of espanso. * * Copyright (C) 2019-2021 Federico Terzi * * espanso is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * espanso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with espanso. If not, see . */ use std::{ cell::RefCell, time::{Duration, Instant}, }; use log::info; use super::super::Middleware; use crate::event::{ input::{Key, KeyboardEvent, Status, Variant}, Event, EventType, }; pub struct DisableOptions { pub toggle_key: Option, pub toggle_key_variant: Option, pub toggle_key_maximum_window: Duration, // TODO: toggle shortcut? } pub struct DisableMiddleware { enabled: RefCell, last_toggle_press: RefCell>, options: DisableOptions, } impl DisableMiddleware { pub fn new(options: DisableOptions) -> Self { Self { enabled: RefCell::new(true), last_toggle_press: RefCell::new(None), options, } } } impl Middleware for DisableMiddleware { fn name(&self) -> &'static str { "disable" } fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { let mut has_status_changed = false; let mut enabled = self.enabled.borrow_mut(); match &event.etype { EventType::Keyboard(m_event) => { if is_toggle_key(m_event, &self.options) { let mut last_toggle_press = self.last_toggle_press.borrow_mut(); if let Some(previous_press) = *last_toggle_press { if previous_press.elapsed() < self.options.toggle_key_maximum_window { *enabled = !*enabled; *last_toggle_press = None; has_status_changed = true; } else { *last_toggle_press = Some(Instant::now()); } } else { *last_toggle_press = Some(Instant::now()); } } } EventType::EnableRequest => { *enabled = true; has_status_changed = true; } EventType::DisableRequest => { *enabled = false; has_status_changed = true; } _ => {} } if has_status_changed { info!("toggled enabled state, is_enabled = {}", *enabled); dispatch(Event::caused_by( event.source_id, if *enabled { EventType::Enabled } else { EventType::Disabled }, )) } // Block keyboard events when disabled if let EventType::Keyboard(_) = &event.etype { if !*enabled { return Event::caused_by(event.source_id, EventType::NOOP); } } // TODO: also ignore hotkey and mouse events event } } fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool { if event.status != Status::Released { return false; } if options .toggle_key .as_ref() .map(|key| key == &event.key) .unwrap_or(false) { if let (Some(variant), Some(e_variant)) = (&options.toggle_key_variant, &event.variant) { variant == e_variant } else { true } } else { false } } // TODO: test