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