Skip to content

Commit

Permalink
feat(mouse): support native double click for windows (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
fu050409 authored Sep 9, 2024
1 parent 03a3237 commit 891490b
Show file tree
Hide file tree
Showing 17 changed files with 1,487 additions and 134 deletions.
222 changes: 121 additions & 101 deletions Cargo.lock

Large diffs are not rendered by default.

37 changes: 6 additions & 31 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
[package]
name = "rshotkey"
version = "0.1.1"
authors = ["苏向夜 <fu050409@163.com>"]
description = "A Hotkey tracker and handler"
repository = "https://github.com/fu050409/rshotkey"
readme = "README.md"
license = "AGPL-3.0"
keywords = ["hotkey"]
edition = "2021"
[workspace]
members = ["crates/*"]
resolver = "2"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.87"
env_logger = "0.11.5"
futures = "0.3.30"
log = "0.4.22"
rayon = "1.10.0"
rdev = "0.5.3"
thiserror = "1.0.63"
tokio = { version = "1.39.3", features = ["full"] }

[lib]
name = "rshotkey"

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }

[[bench]]
name = "history"
harness = false
[workspace.dependencies]
rshotkey-mouse = { path = "crates/rshotkey-mouse" }
tokio = { version = "1.40.0", features = ["full"] }
13 changes: 13 additions & 0 deletions crates/rshotkey-mouse/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "rshotkey-mouse"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.87"
tokio = { workspace = true }
winapi = { version = "0.3.9", features = [
"errhandlingapi",
"libloaderapi",
"winuser",
] }
2 changes: 2 additions & 0 deletions crates/rshotkey-mouse/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(windows)]
pub mod utils;
90 changes: 90 additions & 0 deletions crates/rshotkey-mouse/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use anyhow::{anyhow, Result};
use std::cell::LazyCell;
use std::ptr::null_mut;
use winapi::shared::minwindef::{HINSTANCE, LPARAM, LRESULT, WPARAM};
use winapi::shared::windef::HHOOK;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{
CallNextHookEx, GetDoubleClickTime, GetMessageW, SetWindowsHookExW, UnhookWindowsHookEx,
HC_ACTION, MSLLHOOKSTRUCT, WH_MOUSE_LL, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE,
WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP,
};

static mut HOOK: HHOOK = null_mut();
const DOUBLE_CLICK_TIME: LazyCell<u32> = LazyCell::new(|| unsafe { GetDoubleClickTime() });
static mut LAST_CLICK_TIME: u32 = 0;

unsafe extern "system" fn mouse_hook_proc(code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
if code == HC_ACTION {
let mouse_info = *(l_param as *const MSLLHOOKSTRUCT);
match w_param as u32 {
WM_LBUTTONDOWN => {
if LAST_CLICK_TIME == 0 || mouse_info.time - LAST_CLICK_TIME > *DOUBLE_CLICK_TIME {
println!(
"Left Button Down at ({}, {})",
mouse_info.pt.x, mouse_info.pt.y
);
} else {
println!("Double Click at ({}, {})", mouse_info.pt.x, mouse_info.pt.y);
}
LAST_CLICK_TIME = mouse_info.time;
}
WM_LBUTTONUP => {
println!(
"Left Button Up at ({}, {})",
mouse_info.pt.x, mouse_info.pt.y
);
}
WM_MOUSEMOVE => {
println!("Mouse Move at ({}, {})", mouse_info.pt.x, mouse_info.pt.y);
}
WM_RBUTTONDOWN => {
println!(
"Right Button Down at ({}, {})",
mouse_info.pt.x, mouse_info.pt.y
);
}
WM_RBUTTONUP => {
println!(
"Right Button Up at ({}, {})",
mouse_info.pt.x, mouse_info.pt.y
);
}
WM_MOUSEWHEEL => {}
_ => {
panic!("Unknown message: {}", w_param);
}
}
}
CallNextHookEx(HOOK, code, w_param, l_param)
}

pub unsafe fn set_mouse_hook(handle: HINSTANCE) -> Result<()> {
let hook = SetWindowsHookExW(WH_MOUSE_LL, Some(mouse_hook_proc), handle, 0);
if hook.is_null() {
let error = GetLastError();
return Err(anyhow!(error));
}
HOOK = hook;
Ok(())
}

pub fn listen() -> Result<()> {
unsafe {
let handle = GetModuleHandleW(null_mut());
if let Err(error) = set_mouse_hook(handle) {
println!("Error: {}", error);
};
GetMessageW(null_mut(), null_mut(), 0, 0);
}
Ok(())
}

pub fn close() -> Result<()> {
unsafe {
UnhookWindowsHookEx(HOOK);
HOOK = null_mut();
}
Ok(())
}
Loading

0 comments on commit 891490b

Please sign in to comment.