Skip to content

Commit

Permalink
Improved event loop with using callback
Browse files Browse the repository at this point in the history
  • Loading branch information
davystrong committed Sep 7, 2021
1 parent 264935a commit 6dbf047
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 132 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ edition = "2018"
clipboard-win = "4.2.1"
winapi = {version = "0.3.9", features = ["winuser", "std", "impl-default"]}
error-code = "2.3.0"
lazy_static = "1.4.0"
clap = "3.0.0-beta.4"
230 changes: 104 additions & 126 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
pub mod cli;
pub mod key_utils;
pub mod winapi_abstractions;
pub mod winapi_functions;

use cli::Opts;
use clipboard_win::{formats, get_clipboard, set_clipboard};
use core::ptr;
use key_utils::is_key_pressed;
use lazy_static::lazy_static;
use std::collections::VecDeque;
use std::ffi::CString;
use std::mem;
use std::sync::{atomic, RwLock};
use winapi::um::winuser;

use crate::{
Expand All @@ -23,130 +22,15 @@ use crate::{

const MAX_RETRIES: u8 = 10;

// Have to use global variables to allow access from C callback
static INTERNAL_UPDATE: atomic::AtomicBool = atomic::AtomicBool::new(false);
static MAX_HISTORY: atomic::AtomicUsize = atomic::AtomicUsize::new(10);

lazy_static! {
static ref CB_HISTORY: RwLock<VecDeque<String>> = RwLock::new(VecDeque::new());
}

extern "system" fn message_watcher_proc(
h_wnd: *mut winapi::shared::windef::HWND__,
u_msg: u32,
w_param: usize,
l_param: isize,
) -> isize {
let h_wnd = unsafe { &mut *h_wnd };
match u_msg {
winuser::WM_CREATE => {
add_clipboard_format_listener(h_wnd).unwrap();
register_hotkey(
h_wnd,
1,
(winuser::MOD_CONTROL | winuser::MOD_SHIFT) as u32,
'V' as u32,
)
.unwrap();
0
}
winuser::WM_CLIPBOARDUPDATE => {
if !INTERNAL_UPDATE.load(atomic::Ordering::Relaxed) {
if let Ok(clipboard_data) = get_clipboard::<String, _>(formats::Unicode) {
let mut history = CB_HISTORY.write().unwrap();
history.push_front(clipboard_data);
history.truncate(MAX_HISTORY.load(atomic::Ordering::Relaxed));
}
} else {
INTERNAL_UPDATE.store(false, atomic::Ordering::Relaxed);
}
0
}
winuser::WM_HOTKEY => {
if w_param == 1
/*Ctrl + Shift + V*/
{
fn old_state(v_key: i32) -> u32 {
match is_key_pressed(v_key) {
Ok(false) => winuser::KEYEVENTF_KEYUP,
_ => 0,
}
}

match trigger_keys(
&[
winuser::VK_SHIFT as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
winuser::VK_SHIFT as u16,
],
&[
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
old_state(winuser::VK_CONTROL),
old_state('V' as i32),
old_state(winuser::VK_SHIFT),
],
) {
Ok(_) => {
// Sleep for less time than the lowest possible automatic keystroke repeat ((1000ms / 30) * 0.8)
sleep(25);
CB_HISTORY.write().unwrap().pop_front();
if let Some(last_addition) = CB_HISTORY.read().unwrap().front() {
INTERNAL_UPDATE.store(true, atomic::Ordering::Relaxed);
let _ = set_clipboard(formats::Unicode, last_addition);
}
}
Err(_) => {
let mut retries = 0u8;
while let Err(error) = trigger_keys(
&[
winuser::VK_SHIFT as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
],
&[
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
],
) {
if retries >= MAX_RETRIES {
panic!("Could not release keys after {} attemps. Something has gone badly wrong: {}", MAX_RETRIES, error)
}
retries += 1;
sleep(25);
}
}
}
}
0
}
winuser::WM_DESTROY => {
let clipboard_listener_removed = remove_clipboard_format_listener(h_wnd);
unregister_hotkey(h_wnd, 1).unwrap();
clipboard_listener_removed.unwrap();
0
}
_ => unsafe { winuser::DefWindowProcA(h_wnd, u_msg, w_param, l_param) },
}
}

pub fn run(opts: Opts) {
// Move options to global variables
MAX_HISTORY.store(opts.max_history, atomic::Ordering::Relaxed);

// Create and register a class
let class_name = "filo-clipboard_class";
let window_name = "filo-clipboard";

let class_name_c_string = CString::new(class_name).unwrap();
let lp_wnd_class = winuser::WNDCLASSEXA {
cbSize: mem::size_of::<winuser::WNDCLASSEXA>() as u32,
lpfnWndProc: Some(message_watcher_proc),
lpfnWndProc: Some(winuser::DefWindowProcA),
hInstance: ptr::null_mut(),
lpszClassName: class_name_c_string.as_ptr(),
style: 0,
Expand All @@ -161,8 +45,8 @@ pub fn run(opts: Opts) {

register_class_ex_a(&lp_wnd_class).unwrap();

// Create the invisible window
create_window_ex_a(
// Create the message window
let h_wnd = create_window_ex_a(
winuser::WS_EX_LEFT,
class_name,
window_name,
Expand All @@ -178,13 +62,107 @@ pub fn run(opts: Opts) {
)
.unwrap();

// Register the clipboard listener to the message window
add_clipboard_format_listener(h_wnd).unwrap();

// Register the hotkey listener to the message window
register_hotkey(
h_wnd,
1,
(winuser::MOD_CONTROL | winuser::MOD_SHIFT) as u32,
'V' as u32,
)
.unwrap();

// Event loop
let mut cb_history = VecDeque::<String>::new();
let mut internal_update = false;

let mut lp_msg = winuser::MSG::default();
// println!("Ready");
unsafe {
while winuser::GetMessageA(&mut lp_msg, ptr::null_mut(), 0, 0) != 0 {
winuser::TranslateMessage(&lp_msg);
winuser::DispatchMessageA(&lp_msg);
#[cfg(debug_assertions)]
println!("Ready");
while unsafe { winuser::GetMessageA(&mut lp_msg, h_wnd, 0, 0) != 0 } {
// unsafe { winuser::TranslateMessage(&lp_msg) };

match lp_msg.message {
winuser::WM_CLIPBOARDUPDATE => {
if !internal_update {
if let Ok(clipboard_data) = get_clipboard::<String, _>(formats::Unicode) {
cb_history.push_front(clipboard_data);
cb_history.truncate(opts.max_history);
}
} else {
internal_update = false;
}
}
winuser::WM_HOTKEY => {
if lp_msg.wParam == 1
/*Ctrl + Shift + V*/
{
fn old_state(v_key: i32) -> u32 {
match is_key_pressed(v_key) {
Ok(false) => winuser::KEYEVENTF_KEYUP,
_ => 0,
}
}

match trigger_keys(
&[
winuser::VK_SHIFT as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
winuser::VK_SHIFT as u16,
],
&[
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
old_state(winuser::VK_CONTROL),
old_state('V' as i32),
old_state(winuser::VK_SHIFT),
],
) {
Ok(_) => {
// Sleep for less time than the lowest possible automatic keystroke repeat ((1000ms / 30) * 0.8)
sleep(25);
cb_history.pop_front();
if let Some(last_addition) = cb_history.front() {
internal_update = true;
let _ = set_clipboard(formats::Unicode, last_addition);
}
}
Err(_) => {
let mut retries = 0u8;
while let Err(error) = trigger_keys(
&[
winuser::VK_SHIFT as u16,
winuser::VK_CONTROL as u16,
'V' as u16,
],
&[
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
winuser::KEYEVENTF_KEYUP,
],
) {
if retries >= MAX_RETRIES {
panic!("Could not release keys after {} attemps. Something has gone badly wrong: {}", MAX_RETRIES, error)
}
retries += 1;
sleep(25);
}
}
}
}
}
_ => unsafe {
winuser::DefWindowProcA(lp_msg.hwnd, lp_msg.message, lp_msg.wParam, lp_msg.lParam);
},
}
};
}

let _ = unregister_hotkey(h_wnd, 1);
let _ = remove_clipboard_format_listener(h_wnd);
}
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#![windows_subsystem = "windows"]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use clap::Clap;
use filo_clipboard::{cli::Opts, run};

fn main() {
let opts = Opts::parse();

run(opts);
}
}
4 changes: 2 additions & 2 deletions src/winapi_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn create_window_ex_a<'a>(
h_menu: Option<&'a mut winapi::shared::windef::HMENU__>,
h_instance: Option<&'a mut winapi::shared::minwindef::HINSTANCE__>,
lp_param: Option<&'a mut std::ffi::c_void>,
) -> Result<&'a winapi::shared::windef::HWND__, error_code::ErrorCode<error_code::SystemCategory>> {
) -> Result<&'a mut winapi::shared::windef::HWND__, error_code::ErrorCode<error_code::SystemCategory>> {
//Lifetimes assuming worst case scenario
let class_name = CString::new(lp_class_name).unwrap();
let window_name = CString::new(lp_window_name).unwrap();
Expand All @@ -50,7 +50,7 @@ pub fn create_window_ex_a<'a>(
)
} {
h_wnd if h_wnd.is_null() => Err(SystemError::last()),
h_wnd => Ok(unsafe { &*h_wnd }),
h_wnd => Ok(unsafe { &mut *h_wnd }),
}
}

Expand Down

0 comments on commit 6dbf047

Please sign in to comment.