use super::hotkeys::{Hotkey, HotkeyHandler, Key, Modifiers};
use crate::error::{RdesktopError, Result};
use std::collections::HashMap;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{RegisterHotKey, UnregisterHotKey};
use windows_sys::Win32::UI::WindowsAndMessaging::{
DispatchMessageW, GetMessageW, PostQuitMessage, TranslateMessage, MSG, WM_HOTKEY,
};
const VK_SPACE: u32 = 0x20;
const VK_RETURN: u32 = 0x0D;
const VK_ESCAPE: u32 = 0x1B;
const VK_TAB: u32 = 0x09;
const VK_BACK: u32 = 0x08;
const VK_DELETE: u32 = 0x2E;
const VK_UP: u32 = 0x26;
const VK_DOWN: u32 = 0x28;
const VK_LEFT: u32 = 0x25;
const VK_RIGHT: u32 = 0x27;
const VK_HOME: u32 = 0x24;
const VK_END: u32 = 0x23;
const VK_PRIOR: u32 = 0x21; const VK_NEXT: u32 = 0x22; const VK_INSERT: u32 = 0x2D;
const MOD_ALT: u32 = 0x0001;
const MOD_CONTROL: u32 = 0x0002;
const MOD_SHIFT: u32 = 0x0004;
const MOD_WIN: u32 = 0x0008;
pub struct WinHotkey {
tx: mpsc::Sender<Cmd>,
thread: Option<thread::JoinHandle<()>>,
}
enum Cmd {
Register(u32, Hotkey),
Unregister(u32),
Stop,
}
pub fn key_to_vk(key: Key) -> u32 {
match key {
Key::Letter(c) => c.to_ascii_uppercase() as u32, Key::Digit(d) => 0x30 + d as u32, Key::F(n) => 0x70 + (n as u32 - 1), Key::Space => VK_SPACE,
Key::Enter => VK_RETURN,
Key::Escape => VK_ESCAPE,
Key::Tab => VK_TAB,
Key::Backspace => VK_BACK,
Key::Delete => VK_DELETE,
Key::ArrowUp => VK_UP,
Key::ArrowDown => VK_DOWN,
Key::ArrowLeft => VK_LEFT,
Key::ArrowRight => VK_RIGHT,
Key::Home => VK_HOME,
Key::End => VK_END,
Key::PageUp => VK_PRIOR,
Key::PageDown => VK_NEXT,
Key::Insert => VK_INSERT,
}
}
pub fn mods_to_raw(m: Modifiers) -> u32 {
let mut r = 0u32;
if m.ctrl {
r |= MOD_CONTROL;
}
if m.alt {
r |= MOD_ALT;
}
if m.shift {
r |= MOD_SHIFT;
}
if m.meta {
r |= MOD_WIN;
}
r
}
impl WinHotkey {
pub fn start(handler: Arc<dyn HotkeyHandler>) -> Result<Self> {
let (tx, rx) = mpsc::channel::<Cmd>();
let registry: Arc<Mutex<HashMap<u32, Hotkey>>> = Arc::new(Mutex::new(HashMap::new()));
let registry_th = registry.clone();
let thread = thread::spawn(move || {
let registry = registry_th;
loop {
while let Ok(cmd) = rx.try_recv() {
match cmd {
Cmd::Register(id, hk) => {
let vk = key_to_vk(hk.key);
let mods = mods_to_raw(hk.modifiers);
unsafe {
RegisterHotKey(0, id as i32, mods, vk);
}
registry.lock().unwrap().insert(id, hk);
}
Cmd::Unregister(id) => {
unsafe {
UnregisterHotKey(0, id as i32);
}
registry.lock().unwrap().remove(&id);
}
Cmd::Stop => {
unsafe {
PostQuitMessage(0);
}
break;
}
}
}
let mut msg = unsafe { std::mem::zeroed::<MSG>() };
let res = unsafe { GetMessageW(&mut msg, 0, 0, 0) };
if res == 0 || res == -1 {
break; }
if msg.message == WM_HOTKEY {
let id = msg.wParam as u32;
let hk = registry.lock().unwrap().get(&id).copied();
if let Some(hk) = hk {
handler.on_hotkey(id, &hk);
}
}
unsafe {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
});
Ok(Self {
tx,
thread: Some(thread),
})
}
pub fn register(&self, id: u32, hotkey: &Hotkey) -> Result<()> {
self.tx
.send(Cmd::Register(id, *hotkey))
.map_err(|e| RdesktopError::GlobalInput(format!("hotkey channel: {e}")))
}
pub fn unregister(&self, id: u32) -> Result<()> {
self.tx
.send(Cmd::Unregister(id))
.map_err(|e| RdesktopError::GlobalInput(format!("hotkey channel: {e}")))
}
pub fn stop(mut self) {
let _ = self.tx.send(Cmd::Stop);
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
}