use crate::rdev::{EventType, Key, KeyboardState, UnicodeInfo};
use crate::windows::common::{get_code, get_scan_code, FALSE, TRUE};
use std::collections::HashMap;
use std::ptr::null_mut;
use winapi::ctypes::c_int;
use winapi::shared::minwindef::{BYTE, HKL, LPARAM, UINT};
use winapi::um::processthreadsapi::GetCurrentThreadId;
use winapi::um::winuser::{
self, VK_CONTROL, VK_LCONTROL, VK_LMENU, VK_LWIN, VK_MENU, VK_RCONTROL, VK_RMENU, VK_RWIN,
};
use winapi::um::winuser::{
GetForegroundWindow, GetKeyState, GetKeyboardLayout, GetKeyboardState,
GetWindowThreadProcessId, ToUnicodeEx, VK_CAPITAL, VK_LSHIFT, VK_RSHIFT, VK_SHIFT,
};
const VK_SHIFT_: usize = VK_SHIFT as usize;
const VK_CAPITAL_: usize = VK_CAPITAL as usize;
const VK_LSHIFT_: usize = VK_LSHIFT as usize;
const VK_RSHIFT_: usize = VK_RSHIFT as usize;
const HIGHBIT: u8 = 0x80;
pub struct Keyboard {
last_code: UINT,
last_scan_code: UINT,
last_state: [BYTE; 256],
modifiers: HashMap<c_int, bool>,
last_is_dead: bool,
event_popup: bool,
}
impl Keyboard {
pub fn new() -> Keyboard {
Keyboard {
last_code: 0,
last_scan_code: 0,
last_state: [0; 256],
modifiers: HashMap::new(),
last_is_dead: false,
event_popup: true,
}
}
pub(crate) fn get_modifier(&self, key: Key) -> bool {
match key {
Key::ShiftLeft => *self.modifiers.get(&VK_LSHIFT).unwrap_or(&false),
Key::ShiftRight => *self.modifiers.get(&VK_RSHIFT).unwrap_or(&false),
Key::ControlLeft => *self.modifiers.get(&VK_LCONTROL).unwrap_or(&false),
Key::ControlRight => *self.modifiers.get(&VK_RCONTROL).unwrap_or(&false),
Key::Alt => *self.modifiers.get(&VK_LMENU).unwrap_or(&false),
Key::AltGr => *self.modifiers.get(&VK_RMENU).unwrap_or(&false),
Key::MetaLeft => *self.modifiers.get(&VK_LWIN).unwrap_or(&false),
Key::MetaRight => *self.modifiers.get(&VK_RWIN).unwrap_or(&false),
_ => false,
}
}
#[inline]
fn set_modifier_(&mut self, key1: c_int, key2: c_int, key: c_int, down: bool) {
self.modifiers.insert(key1, down);
let key_down = if down {
true
} else {
*self.modifiers.get(&key2).unwrap_or(&false)
};
self.modifiers.insert(key, key_down);
}
pub(crate) fn set_modifier(&mut self, key: Key, down: bool) {
match key {
Key::ControlLeft => {
self.set_modifier_(VK_LCONTROL, VK_RCONTROL, VK_CONTROL, down);
}
Key::ControlRight => {
self.set_modifier_(VK_RCONTROL, VK_LCONTROL, VK_CONTROL, down);
}
Key::ShiftLeft => {
self.set_modifier_(VK_LSHIFT, VK_RSHIFT, VK_SHIFT, down);
}
Key::ShiftRight => {
self.set_modifier_(VK_RSHIFT, VK_LSHIFT, VK_SHIFT, down);
}
Key::Alt => {
self.set_modifier_(VK_LMENU, VK_RMENU, VK_MENU, down);
}
Key::AltGr => {
self.set_modifier_(VK_RMENU, VK_LMENU, VK_MENU, down);
}
Key::MetaLeft => {
self.modifiers.insert(VK_LWIN, down);
}
Key::MetaRight => {
self.modifiers.insert(VK_RWIN, down);
}
_ => {
}
}
}
#[inline]
pub(crate) fn set_event_popup(&mut self, b: bool) {
self.event_popup = b;
}
pub(crate) unsafe fn get_unicode(&mut self, lpdata: LPARAM) -> Option<UnicodeInfo> {
let code = get_code(lpdata);
let scan_code = get_scan_code(lpdata);
self.set_global_state()?;
self.get_code_name_unicode(code, scan_code)
}
pub(crate) unsafe fn set_global_state(&mut self) -> Option<()> {
let mut state = [0_u8; 256];
let state_ptr = state.as_mut_ptr();
let _shift = GetKeyState(VK_SHIFT);
let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
let thread_id = GetCurrentThreadId();
let status = if winuser::AttachThreadInput(thread_id, current_window_thread_id, TRUE) == 1 {
let status = GetKeyboardState(state_ptr);
winuser::AttachThreadInput(thread_id, current_window_thread_id, FALSE);
status
} else {
GetKeyboardState(state_ptr)
};
if status != 1 {
return None;
}
let press_state = 129;
let control_left = self.get_modifier(Key::ControlLeft);
let control_right = self.get_modifier(Key::ControlRight);
if control_left {
state[VK_LCONTROL as usize] = press_state;
}
if control_right {
state[VK_RCONTROL as usize] = press_state;
}
if control_left || control_right {
state[VK_CONTROL as usize] = press_state;
}
let shift_left = self.get_modifier(Key::ShiftLeft);
let shift_right = self.get_modifier(Key::ShiftRight);
if shift_left {
state[VK_LSHIFT as usize] = press_state;
}
if shift_right {
state[VK_RSHIFT as usize] = press_state;
}
if shift_left || shift_right {
state[VK_SHIFT as usize] = press_state;
}
let alt_left = self.get_modifier(Key::Alt);
let alt_right = self.get_modifier(Key::AltGr);
if alt_left {
state[VK_LMENU as usize] = press_state;
}
if alt_right {
state[VK_RMENU as usize] = press_state;
}
if alt_left || alt_right {
state[VK_MENU as usize] = press_state;
}
let win_left = self.get_modifier(Key::MetaLeft);
let win_right = self.get_modifier(Key::MetaRight);
if win_left {
state[VK_LWIN as usize] = press_state;
}
if win_right {
state[VK_RWIN as usize] = press_state;
}
self.last_state = state;
Some(())
}
pub(crate) unsafe fn get_code_name_unicode(
&mut self,
code: UINT,
scan_code: UINT,
) -> Option<UnicodeInfo> {
let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
let state_ptr = self.last_state.as_mut_ptr();
const BUF_LEN: i32 = 32;
let mut buff = [0_u16; BUF_LEN as usize];
let buff_ptr = buff.as_mut_ptr();
let layout = GetKeyboardLayout(current_window_thread_id);
let len = ToUnicodeEx(code, scan_code, state_ptr, buff_ptr, 8 - 1, 0, layout);
let mut is_dead = false;
let result = match len {
0 => None,
-1 => {
is_dead = true;
self.clear_keyboard_buffer(code, scan_code, layout);
Some(UnicodeInfo {
name: None,
unicode: Vec::new(),
is_dead: true,
})
}
len if len > 0 => {
let unicode = buff[..len as usize].to_vec();
Some(UnicodeInfo {
name: String::from_utf16(&unicode).ok(),
unicode,
is_dead: false,
})
}
_ => None,
};
if self.event_popup {
if self.last_code != 0 && self.last_is_dead {
buff = [0; 32];
let buff_ptr = buff.as_mut_ptr();
let last_state_ptr = self.last_state.as_mut_ptr();
ToUnicodeEx(
self.last_code,
self.last_scan_code,
last_state_ptr,
buff_ptr,
BUF_LEN,
0,
layout,
);
self.last_code = 0;
} else {
self.last_code = code;
self.last_scan_code = scan_code;
}
} else {
if is_dead {
buff = [0; 32];
let buff_ptr = buff.as_mut_ptr();
let last_state_ptr = self.last_state.as_mut_ptr();
ToUnicodeEx(
code,
scan_code,
last_state_ptr,
buff_ptr,
BUF_LEN,
0,
layout,
);
self.last_code = 0;
} else {
self.last_code = code;
self.last_scan_code = scan_code;
}
}
self.last_is_dead = is_dead;
if len == 1
&& matches!(
String::from_utf16(&buff[..len as usize])
.ok()?
.chars()
.next()?,
'\u{1}'..='\u{1f}'
)
{
return None;
}
result
}
unsafe fn clear_keyboard_buffer(&self, code: UINT, scan_code: UINT, layout: HKL) {
const BUF_LEN: i32 = 32;
let mut buff = [0_u16; BUF_LEN as usize];
let buff_ptr = buff.as_mut_ptr();
let mut state = [0_u8; 256];
let state_ptr = state.as_mut_ptr();
let mut len = -1;
while len < 0 {
len = ToUnicodeEx(code, scan_code, state_ptr, buff_ptr, BUF_LEN, 0, layout);
}
}
pub fn is_dead(&mut self) -> bool {
self.last_is_dead
}
}
impl KeyboardState for Keyboard {
fn add(&mut self, event_type: &EventType) -> Option<UnicodeInfo> {
match event_type {
EventType::KeyPress(key) => match key {
Key::ShiftLeft => {
self.last_state[VK_SHIFT_] |= HIGHBIT;
self.last_state[VK_LSHIFT_] |= HIGHBIT;
None
}
Key::ShiftRight => {
self.last_state[VK_SHIFT_] |= HIGHBIT;
self.last_state[VK_RSHIFT_] |= HIGHBIT;
None
}
Key::CapsLock => {
self.last_state[VK_CAPITAL_] ^= HIGHBIT;
None
}
key => {
let (code, scan_code) = crate::get_win_codes(*key)?;
unsafe {
let _control = GetKeyState(winuser::VK_CONTROL) & 0x8000_u16 as i16;
let _altgr = GetKeyState(winuser::VK_RMENU) & 0x8000_u16 as i16;
if _control < 0 && _altgr >= 0 {
self.get_code_name_unicode(code as _, scan_code)
} else {
self.set_global_state()?;
self.get_code_name_unicode(code, scan_code)
}
}
}
},
EventType::KeyRelease(key) => match key {
Key::ShiftLeft => {
self.last_state[VK_SHIFT_] &= !HIGHBIT;
self.last_state[VK_LSHIFT_] &= !HIGHBIT;
None
}
Key::ShiftRight => {
self.last_state[VK_SHIFT_] &= !HIGHBIT;
self.last_state[VK_RSHIFT_] &= HIGHBIT;
None
}
_ => None,
},
_ => None,
}
}
}