First draft of working passive mode on linux

This commit is contained in:
Federico Terzi 2020-01-19 00:30:30 +01:00
parent 9e5a2a7c95
commit 9332899969
10 changed files with 92 additions and 21 deletions

View File

@ -307,6 +307,14 @@ void trigger_alt_shift_ins_paste() {
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Shift+Alt+Insert", 8000); xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Shift+Alt+Insert", 8000);
} }
void trigger_copy() {
// Release the other keys, for an explanation, read the 'trigger_paste' method
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Alt", 8000);
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Shift", 8000);
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Control_L+c", 8000);
}
// SYSTEM MODULE // SYSTEM MODULE
// Function taken from the wmlib tool source code // Function taken from the wmlib tool source code

View File

@ -92,6 +92,10 @@ extern "C" void trigger_shift_ins_paste();
*/ */
extern "C" void trigger_alt_shift_ins_paste(); extern "C" void trigger_alt_shift_ins_paste();
/*
* Trigger copy shortcut ( Pressing CTRL+C )
*/
extern "C" void trigger_copy();
// SYSTEM MODULE // SYSTEM MODULE

View File

@ -44,4 +44,5 @@ extern {
pub fn trigger_terminal_paste(); pub fn trigger_terminal_paste();
pub fn trigger_shift_ins_paste(); pub fn trigger_shift_ins_paste();
pub fn trigger_alt_shift_ins_paste(); pub fn trigger_alt_shift_ins_paste();
pub fn trigger_copy();
} }

View File

@ -53,10 +53,12 @@ fn default_use_system_agent() -> bool { true }
fn default_config_caching_interval() -> i32 { 800 } fn default_config_caching_interval() -> i32 { 800 }
fn default_word_separators() -> Vec<char> { vec![' ', ',', '.', '\r', '\n', 22u8 as char] } fn default_word_separators() -> Vec<char> { vec![' ', ',', '.', '\r', '\n', 22u8 as char] }
fn default_toggle_interval() -> u32 { 230 } fn default_toggle_interval() -> u32 { 230 }
fn default_toggle_key() -> KeyModifier { KeyModifier::ALT }
fn default_preserve_clipboard() -> bool {false} fn default_preserve_clipboard() -> bool {false}
fn default_passive_match_regex() -> String{ "(?P<name>:\\p{L}+)(/(?P<args>.*)/)?".to_owned() } fn default_passive_match_regex() -> String{ "(?P<name>:\\p{L}+)(/(?P<args>.*)/)?".to_owned() }
fn default_passive_arg_delimiter() -> char { '/' } fn default_passive_arg_delimiter() -> char { '/' }
fn default_passive_arg_escape() -> char { '\\' } fn default_passive_arg_escape() -> char { '\\' }
fn default_passive_key() -> KeyModifier { KeyModifier::OFF }
fn default_backspace_limit() -> i32 { 3 } fn default_backspace_limit() -> i32 { 3 }
fn default_exclude_default_matches() -> bool {false} fn default_exclude_default_matches() -> bool {false}
fn default_matches() -> Vec<Match> { Vec::new() } fn default_matches() -> Vec<Match> { Vec::new() }
@ -96,7 +98,7 @@ pub struct Configs {
#[serde(default = "default_word_separators")] #[serde(default = "default_word_separators")]
pub word_separators: Vec<char>, // TODO: add parsing test pub word_separators: Vec<char>, // TODO: add parsing test
#[serde(default)] #[serde(default = "default_toggle_key")]
pub toggle_key: KeyModifier, pub toggle_key: KeyModifier,
#[serde(default = "default_toggle_interval")] #[serde(default = "default_toggle_interval")]
@ -114,6 +116,9 @@ pub struct Configs {
#[serde(default = "default_passive_arg_escape")] #[serde(default = "default_passive_arg_escape")]
pub passive_arg_escape: char, pub passive_arg_escape: char,
#[serde(default = "default_passive_key")]
pub passive_key: KeyModifier,
#[serde(default)] #[serde(default)]
pub paste_shortcut: PasteShortcut, pub paste_shortcut: PasteShortcut,
@ -156,7 +161,7 @@ impl Configs {
validate_field!(result, self.config_caching_interval, default_config_caching_interval()); validate_field!(result, self.config_caching_interval, default_config_caching_interval());
validate_field!(result, self.log_level, default_log_level()); validate_field!(result, self.log_level, default_log_level());
validate_field!(result, self.toggle_key, KeyModifier::default()); validate_field!(result, self.toggle_key, default_toggle_key());
validate_field!(result, self.toggle_interval, default_toggle_interval()); validate_field!(result, self.toggle_interval, default_toggle_interval());
validate_field!(result, self.backspace_limit, default_backspace_limit()); validate_field!(result, self.backspace_limit, default_backspace_limit());
validate_field!(result, self.ipc_server_port, default_ipc_server_port()); validate_field!(result, self.ipc_server_port, default_ipc_server_port());
@ -165,6 +170,7 @@ impl Configs {
validate_field!(result, self.passive_match_regex, default_passive_match_regex()); validate_field!(result, self.passive_match_regex, default_passive_match_regex());
validate_field!(result, self.passive_arg_delimiter, default_passive_arg_delimiter()); validate_field!(result, self.passive_arg_delimiter, default_passive_arg_delimiter());
validate_field!(result, self.passive_arg_escape, default_passive_arg_escape()); validate_field!(result, self.passive_arg_escape, default_passive_arg_escape());
validate_field!(result, self.passive_key, default_passive_key());
result result
} }

View File

@ -231,6 +231,39 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa
self.ui_manager.notify(message); self.ui_manager.notify(message);
} }
fn on_passive(&self) {
info!("Passive mode activated");
// Trigger a copy shortcut to transfer the content of the selection to the clipboard
self.keyboard_manager.trigger_copy();
// Sleep for a while, giving time to effectively copy the text
std::thread::sleep(std::time::Duration::from_millis(100)); // TODO: avoid hardcoding
// Then get the text from the clipboard and render the match output
let clipboard = self.clipboard_manager.get_clipboard();
if let Some(clipboard) = clipboard {
let config = self.config_manager.active_config();
let rendered = self.renderer.render_passive(&clipboard,
&config);
match rendered {
RenderResult::Text(payload) => {
// Paste back the result in the field
self.clipboard_manager.set_clipboard(&payload);
std::thread::sleep(std::time::Duration::from_millis(100)); // TODO: avoid hardcoding
self.keyboard_manager.trigger_paste(&config.paste_shortcut);
},
_ => {
warn!("Cannot expand passive match")
},
}
}
}
} }
impl <'a, S: KeyboardManager, C: ClipboardManager, impl <'a, S: KeyboardManager, C: ClipboardManager,

View File

@ -66,12 +66,6 @@ pub enum KeyModifier {
OFF, OFF,
} }
impl Default for KeyModifier {
fn default() -> Self {
KeyModifier::ALT
}
}
// Receivers // Receivers
pub trait KeyEventReceiver { pub trait KeyEventReceiver {

View File

@ -81,4 +81,10 @@ impl super::KeyboardManager for LinuxKeyboardManager {
left_arrow(count); left_arrow(count);
} }
} }
fn trigger_copy(&self) {
unsafe {
trigger_copy();
}
}
} }

View File

@ -34,6 +34,7 @@ pub trait KeyboardManager {
fn trigger_paste(&self, shortcut: &PasteShortcut); fn trigger_paste(&self, shortcut: &PasteShortcut);
fn delete_string(&self, count: i32); fn delete_string(&self, count: i32);
fn move_cursor_left(&self, count: i32); fn move_cursor_left(&self, count: i32);
fn trigger_copy(&self);
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -181,6 +181,7 @@ pub enum TriggerEntry {
pub trait MatchReceiver { pub trait MatchReceiver {
fn on_match(&self, m: &Match, trailing_separator: Option<char>); fn on_match(&self, m: &Match, trailing_separator: Option<char>);
fn on_enable_update(&self, status: bool); fn on_enable_update(&self, status: bool);
fn on_passive(&self);
} }
pub trait Matcher : KeyEventReceiver { pub trait Matcher : KeyEventReceiver {

View File

@ -18,7 +18,7 @@
*/ */
use crate::matcher::{Match, MatchReceiver, TriggerEntry}; use crate::matcher::{Match, MatchReceiver, TriggerEntry};
use std::cell::RefCell; use std::cell::{RefCell, Ref};
use crate::event::{KeyModifier, ActionEventReceiver, ActionType}; use crate::event::{KeyModifier, ActionEventReceiver, ActionType};
use crate::config::ConfigManager; use crate::config::ConfigManager;
use crate::event::KeyModifier::BACKSPACE; use crate::event::KeyModifier::BACKSPACE;
@ -30,6 +30,7 @@ pub struct ScrollingMatcher<'a, R: MatchReceiver, M: ConfigManager<'a>> {
receiver: &'a R, receiver: &'a R,
current_set_queue: RefCell<VecDeque<Vec<MatchEntry<'a>>>>, current_set_queue: RefCell<VecDeque<Vec<MatchEntry<'a>>>>,
toggle_press_time: RefCell<SystemTime>, toggle_press_time: RefCell<SystemTime>,
passive_press_time: RefCell<SystemTime>,
is_enabled: RefCell<bool>, is_enabled: RefCell<bool>,
was_previous_char_word_separator: RefCell<bool>, was_previous_char_word_separator: RefCell<bool>,
} }
@ -45,12 +46,14 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> ScrollingMatcher<'a, R, M> {
pub fn new(config_manager: &'a M, receiver: &'a R) -> ScrollingMatcher<'a, R, M> { pub fn new(config_manager: &'a M, receiver: &'a R) -> ScrollingMatcher<'a, R, M> {
let current_set_queue = RefCell::new(VecDeque::new()); let current_set_queue = RefCell::new(VecDeque::new());
let toggle_press_time = RefCell::new(SystemTime::now()); let toggle_press_time = RefCell::new(SystemTime::now());
let passive_press_time = RefCell::new(SystemTime::now());
ScrollingMatcher{ ScrollingMatcher{
config_manager, config_manager,
receiver, receiver,
current_set_queue, current_set_queue,
toggle_press_time, toggle_press_time,
passive_press_time,
is_enabled: RefCell::new(true), is_enabled: RefCell::new(true),
was_previous_char_word_separator: RefCell::new(true), was_previous_char_word_separator: RefCell::new(true),
} }
@ -193,22 +196,25 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMa
fn handle_modifier(&self, m: KeyModifier) { fn handle_modifier(&self, m: KeyModifier) {
let config = self.config_manager.default_config(); let config = self.config_manager.default_config();
// TODO: at the moment, activating the passive key triggers the toggle key
// study a mechanism to avoid this problem
if m == config.toggle_key { if m == config.toggle_key {
if m == KeyModifier::OFF { return } check_interval(&self.toggle_press_time,
let mut toggle_press_time = self.toggle_press_time.borrow_mut(); u128::from(config.toggle_interval), || {
if let Ok(elapsed) = toggle_press_time.elapsed() { self.toggle();
if elapsed.as_millis() < u128::from(config.toggle_interval) {
self.toggle();
let is_enabled = self.is_enabled.borrow(); let is_enabled = self.is_enabled.borrow();
if !*is_enabled { if !*is_enabled {
self.current_set_queue.borrow_mut().clear(); self.current_set_queue.borrow_mut().clear();
}
} }
} });
}else if m == config.passive_key {
(*toggle_press_time) = SystemTime::now(); check_interval(&self.passive_press_time,
u128::from(config.toggle_interval), || {
self.receiver.on_passive();
});
} }
// Backspace handling, basically "rewinding history" // Backspace handling, basically "rewinding history"
@ -234,4 +240,15 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> ActionEventReceiver for Scroll
_ => {} _ => {}
} }
} }
}
fn check_interval<F>(state_var: &RefCell<SystemTime>, interval: u128, elapsed_callback: F) where F:Fn() {
let mut press_time = state_var.borrow_mut();
if let Ok(elapsed) = press_time.elapsed() {
if elapsed.as_millis() < interval {
elapsed_callback();
}
}
(*press_time) = SystemTime::now();
} }