diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp index a9b3069..9c72d7f 100644 --- a/native/libwinbridge/bridge.cpp +++ b/native/libwinbridge/bridge.cpp @@ -199,5 +199,30 @@ void send_string(const wchar_t * string) { vec.push_back(input); } + SendInput(vec.size(), vec.data(), sizeof(INPUT)); +} + +/* + * Send the backspace keypress, *count* times. + */ +void delete_string(int32_t count) { + std::vector vec; + + for (int i = 0; i < count; i++) { + INPUT input = { 0 }; + + input.type = INPUT_KEYBOARD; + input.ki.wScan = 0; + input.ki.time = 0; + input.ki.dwExtraInfo = 0; + input.ki.wVk = VK_BACK; + input.ki.dwFlags = 0; // 0 for key press + vec.push_back(input); + + // Release the "A" key + input.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release + vec.push_back(input); + } + SendInput(vec.size(), vec.data(), sizeof(INPUT)); } \ No newline at end of file diff --git a/native/libwinbridge/bridge.h b/native/libwinbridge/bridge.h index dba6259..a06a999 100644 --- a/native/libwinbridge/bridge.h +++ b/native/libwinbridge/bridge.h @@ -34,4 +34,9 @@ extern "C" void eventloop(); */ extern "C" void send_string(const wchar_t * string); +/* + * Send the backspace keypress, *count* times. + */ +extern "C" void delete_string(int32_t count); + #endif //ESPANSO_BRIDGE_H diff --git a/src/engine.rs b/src/engine.rs index e69de29..59a04d6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -0,0 +1,19 @@ +use crate::matcher::{Match, MatchReceiver}; +use crate::keyboard::KeyboardSender; + +pub struct Engine<'a>{ + sender: &'a KeyboardSender +} + +impl <'a> Engine<'a> { + pub fn new(sender: &'a KeyboardSender) -> Engine<'a> { + Engine{sender} + } +} + +impl <'a> MatchReceiver for Engine<'a>{ + fn on_match(&self, m: Match) { + self.sender.delete_string(m.trigger.len() as i32); + self.sender.send_string(m.result.as_str()); + } +} \ No newline at end of file diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index 0ecfd9f..2efaebf 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -10,9 +10,15 @@ pub trait KeyboardInterceptor { pub trait KeyboardSender { fn send_string(&self, s: &str); + fn delete_string(&self, count: i32); } #[cfg(target_os = "windows")] -pub fn get_backend(sender: mpsc::Sender) -> (impl KeyboardInterceptor, impl KeyboardSender) { - (windows::WindowsKeyboardInterceptor {sender}, windows::WindowsKeyboardSender{}) +pub fn get_interceptor(sender: mpsc::Sender) -> impl KeyboardInterceptor { + windows::WindowsKeyboardInterceptor {sender} +} + +#[cfg(target_os = "windows")] +pub fn get_sender() -> impl KeyboardSender { + windows::WindowsKeyboardSender{} } \ No newline at end of file diff --git a/src/keyboard/windows.rs b/src/keyboard/windows.rs index 5490324..201169a 100644 --- a/src/keyboard/windows.rs +++ b/src/keyboard/windows.rs @@ -1,6 +1,6 @@ use std::thread; use std::sync::mpsc; -use widestring::{WideString, WideStr}; +use widestring::{U16CString}; #[repr(C)] pub struct WindowsKeyboardInterceptor { @@ -29,9 +29,21 @@ pub struct WindowsKeyboardSender { impl super::KeyboardSender for WindowsKeyboardSender { fn send_string(&self, s: &str) { - let s = WideString::from(s.to_owned()); + let res = U16CString::from_str(s); + match res { + Ok(s) => { + unsafe { + send_string(s.as_ptr()); + } + } + Err(e) => println!("Error while sending string: {}", e.to_string()) + } + + } + + fn delete_string(&self, count: i32) { unsafe { - send_string(s.as_ptr()); + delete_string(count) } } } @@ -49,10 +61,12 @@ extern fn keypress_callback(_self: *mut WindowsKeyboardInterceptor, raw_buffer: } } +#[allow(improper_ctypes)] #[link(name="winbridge", kind="static")] extern { fn register_keypress_callback(s: *const WindowsKeyboardInterceptor, cb: extern fn(_self: *mut WindowsKeyboardInterceptor, *const i32, i32)); fn initialize_window(); fn eventloop(); fn send_string(string: *const u16); + fn delete_string(count: i32); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 59e8bff..3b68f05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,26 @@ -use std::thread::sleep; -use std::time::Duration; use std::sync::mpsc; use crate::keyboard::KeyboardInterceptor; -use crate::keyboard::KeyboardSender; use crate::matcher::Matcher; +use crate::matcher::scrolling::ScrollingMatcher; +use crate::engine::Engine; mod keyboard; mod matcher; +mod engine; fn main() { - println!("Hello, world from Rust!"); + println!("espanso is running!"); - let (sender, receiver) = mpsc::channel(); + let (txc, rxc) = mpsc::channel(); - let (interceptor, sender) = keyboard::get_backend(sender); + let interceptor = keyboard::get_interceptor(txc); interceptor.initialize(); interceptor.start(); - let matcher = Matcher{receiver}; - matcher.watch(); + let sender = keyboard::get_sender(); + + let engine = Engine::new(&sender); + + let matcher = ScrollingMatcher::new(&engine); + matcher.watch(&rxc); } \ No newline at end of file diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs index 9aa9235..07f9f47 100644 --- a/src/matcher/mod.rs +++ b/src/matcher/mod.rs @@ -1,24 +1,25 @@ use std::sync::mpsc::Receiver; +pub(crate) mod scrolling; + pub struct Match { pub trigger: String, pub result: String } - - -pub struct Matcher { - pub receiver: Receiver +pub trait MatchReceiver { + fn on_match(&self, m: Match); } -impl Matcher { - pub fn watch(&self) { +pub trait Matcher { + fn handle_char(&self, c: char); + fn watch(&self, receiver: &Receiver) { loop { - match self.receiver.recv() { + match receiver.recv() { Ok(c) => { - println!("Yeah {}",c); + self.handle_char(c); }, - Err(_) => panic!("Worker threads disconnected before the solution was found!"), + Err(_) => panic!("Keyboard interceptor broke receiver stream."), } } } diff --git a/src/matcher/scrolling.rs b/src/matcher/scrolling.rs index e69de29..14023dd 100644 --- a/src/matcher/scrolling.rs +++ b/src/matcher/scrolling.rs @@ -0,0 +1,21 @@ +use crate::matcher::{Match, MatchReceiver}; + +pub struct ScrollingMatcher<'a>{ + receiver: &'a MatchReceiver +} + +impl <'a> super::Matcher for ScrollingMatcher<'a> { + fn handle_char(&self, c: char) { + println!("Scroll {}",c); + + if c == 'a' { + self.receiver.on_match(Match{trigger: "a".to_owned(), result: "ciao".to_owned()}); + } + } +} + +impl <'a> ScrollingMatcher<'a> { + pub fn new(receiver: &'a MatchReceiver) -> ScrollingMatcher<'a> { + ScrollingMatcher{ receiver } + } +} \ No newline at end of file