rdev/windows/
keyboard.rs

1use crate::rdev::{EventType, Key, KeyboardState};
2use crate::windows::common::{get_code, get_scan_code, FALSE, TRUE};
3use crate::windows::keycodes::code_from_key;
4use std::ptr::null_mut;
5use winapi::shared::minwindef::{BYTE, HKL, LPARAM, UINT};
6use winapi::um::processthreadsapi::GetCurrentThreadId;
7use winapi::um::winuser;
8use winapi::um::winuser::{
9    GetForegroundWindow, GetKeyState, GetKeyboardLayout, GetKeyboardState,
10    GetWindowThreadProcessId, ToUnicodeEx, VK_CAPITAL, VK_LSHIFT, VK_RSHIFT, VK_SHIFT,
11};
12
13const VK_SHIFT_: usize = VK_SHIFT as usize;
14const VK_CAPITAL_: usize = VK_CAPITAL as usize;
15const VK_LSHIFT_: usize = VK_LSHIFT as usize;
16const VK_RSHIFT_: usize = VK_RSHIFT as usize;
17const HIGHBIT: u8 = 0x80;
18
19pub struct Keyboard {
20    last_code: UINT,
21    last_scan_code: UINT,
22    last_state: [BYTE; 256],
23    last_is_dead: bool,
24}
25
26impl Keyboard {
27    pub fn new() -> Option<Keyboard> {
28        Some(Keyboard {
29            last_code: 0,
30            last_scan_code: 0,
31            last_state: [0; 256],
32            last_is_dead: false,
33        })
34    }
35
36    pub(crate) unsafe fn get_name(&mut self, lpdata: LPARAM) -> Option<String> {
37        // https://gist.github.com/akimsko/2011327
38        // https://www.experts-exchange.com/questions/23453780/LowLevel-Keystroke-Hook-removes-Accents-on-French-Keyboard.html
39        let code = get_code(lpdata);
40        let scan_code = get_scan_code(lpdata);
41
42        self.set_global_state()?;
43        self.get_code_name(code, scan_code)
44    }
45
46    pub(crate) unsafe fn set_global_state(&mut self) -> Option<()> {
47        let mut state = [0_u8; 256];
48        let state_ptr = state.as_mut_ptr();
49
50        let _shift = GetKeyState(VK_SHIFT);
51        let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
52        let thread_id = GetCurrentThreadId();
53        // Attach to active thread so we can get that keyboard state
54        let status = if winuser::AttachThreadInput(thread_id, current_window_thread_id, TRUE) == 1 {
55            // Current state of the modifiers in keyboard
56            let status = GetKeyboardState(state_ptr);
57
58            // Detach
59            winuser::AttachThreadInput(thread_id, current_window_thread_id, FALSE);
60            status
61        } else {
62            // Could not attach, perhaps it is this process?
63            GetKeyboardState(state_ptr)
64        };
65
66        if status != 1 {
67            return None;
68        }
69        self.last_state = state;
70        Some(())
71    }
72
73    pub(crate) unsafe fn get_code_name(&mut self, code: UINT, scan_code: UINT) -> Option<String> {
74        let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
75        let state_ptr = self.last_state.as_mut_ptr();
76        const BUF_LEN: i32 = 32;
77        let mut buff = [0_u16; BUF_LEN as usize];
78        let buff_ptr = buff.as_mut_ptr();
79        let layout = GetKeyboardLayout(current_window_thread_id);
80        let len = ToUnicodeEx(code, scan_code, state_ptr, buff_ptr, 8 - 1, 0, layout);
81
82        let mut is_dead = false;
83        let result = match len {
84            0 => None,
85            -1 => {
86                is_dead = true;
87                self.clear_keyboard_buffer(code, scan_code, layout);
88                None
89            }
90            len if len > 0 => String::from_utf16(&buff[..len as usize]).ok(),
91            _ => None,
92        };
93
94        if self.last_code != 0 && self.last_is_dead {
95            buff = [0; 32];
96            let buff_ptr = buff.as_mut_ptr();
97            let last_state_ptr = self.last_state.as_mut_ptr();
98            ToUnicodeEx(
99                self.last_code,
100                self.last_scan_code,
101                last_state_ptr,
102                buff_ptr,
103                BUF_LEN,
104                0,
105                layout,
106            );
107            self.last_code = 0;
108        } else {
109            self.last_code = code;
110            self.last_scan_code = scan_code;
111            self.last_is_dead = is_dead;
112        }
113        result
114    }
115
116    unsafe fn clear_keyboard_buffer(&self, code: UINT, scan_code: UINT, layout: HKL) {
117        const BUF_LEN: i32 = 32;
118        let mut buff = [0_u16; BUF_LEN as usize];
119        let buff_ptr = buff.as_mut_ptr();
120        let mut state = [0_u8; 256];
121        let state_ptr = state.as_mut_ptr();
122
123        let mut len = -1;
124        while len < 0 {
125            len = ToUnicodeEx(code, scan_code, state_ptr, buff_ptr, BUF_LEN, 0, layout);
126        }
127    }
128}
129
130impl KeyboardState for Keyboard {
131    fn add(&mut self, event_type: &EventType) -> Option<String> {
132        match event_type {
133            EventType::KeyPress(key) => match key {
134                Key::ShiftLeft => {
135                    self.last_state[VK_SHIFT_] |= HIGHBIT;
136                    self.last_state[VK_LSHIFT_] |= HIGHBIT;
137                    None
138                }
139                Key::ShiftRight => {
140                    self.last_state[VK_SHIFT_] |= HIGHBIT;
141                    self.last_state[VK_RSHIFT_] |= HIGHBIT;
142                    None
143                }
144                Key::CapsLock => {
145                    self.last_state[VK_CAPITAL_] ^= HIGHBIT;
146                    None
147                }
148                key => {
149                    let code = code_from_key(*key)?;
150                    unsafe { self.get_code_name(code.into(), 0) }
151                }
152            },
153            EventType::KeyRelease(key) => match key {
154                Key::ShiftLeft => {
155                    self.last_state[VK_SHIFT_] &= !HIGHBIT;
156                    self.last_state[VK_LSHIFT_] &= !HIGHBIT;
157                    None
158                }
159                Key::ShiftRight => {
160                    self.last_state[VK_SHIFT_] &= !HIGHBIT;
161                    self.last_state[VK_RSHIFT_] &= HIGHBIT;
162                    None
163                }
164                _ => None,
165            },
166
167            _ => None,
168        }
169    }
170
171    fn reset(&mut self) {
172        self.last_state[16] = 0;
173        self.last_state[20] = 0;
174    }
175}