use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_0, INPUT_KEYBOARD, KEYBD_EVENT_FLAGS, KEYBDINPUT, KEYEVENTF_KEYUP, SendInput,
VIRTUAL_KEY,
};
const VK_LEFT_KEY: VIRTUAL_KEY = VIRTUAL_KEY(0x25);
const VK_RIGHT_KEY: VIRTUAL_KEY = VIRTUAL_KEY(0x27);
const VK_SHIFT_KEY: VIRTUAL_KEY = VIRTUAL_KEY(0x10);
pub struct KeySequence {
pressed: Vec<VIRTUAL_KEY>,
}
impl KeySequence {
pub fn new() -> Self {
Self {
pressed: Vec::new(),
}
}
pub fn down(&mut self, vk: VIRTUAL_KEY) -> bool {
send_key(vk, false).then(|| self.pressed.push(vk)).is_some()
}
pub fn tap(vk: VIRTUAL_KEY) -> bool {
send_key(vk, false) && send_key(vk, true)
}
}
impl Default for KeySequence {
fn default() -> Self {
Self::new()
}
}
impl Drop for KeySequence {
fn drop(&mut self) {
for vk in self.pressed.drain(..).rev() {
let _ = send_key(vk, true);
}
}
}
pub fn send_text_unicode(text: &str) -> bool {
use windows::Win32::UI::Input::KeyboardAndMouse::KEYEVENTF_UNICODE;
fn unicode_input(unit: u16, key_up: bool) -> INPUT {
INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 {
ki: KEYBDINPUT {
wVk: VIRTUAL_KEY(0),
wScan: unit,
dwFlags: if key_up {
KEYEVENTF_UNICODE | KEYEVENTF_KEYUP
} else {
KEYEVENTF_UNICODE
},
time: 0,
dwExtraInfo: 0,
},
},
}
}
let units: Vec<u16> = text.encode_utf16().collect();
if units.is_empty() {
return true;
}
let inputs: Vec<INPUT> = units
.into_iter()
.flat_map(|u| [unicode_input(u, false), unicode_input(u, true)])
.collect();
let Some(input_size) = input_struct_size_i32() else {
return false;
};
let sent = unsafe { SendInput(&inputs, input_size) } as usize;
sent == inputs.len()
}
pub fn reselect_last_inserted_text_utf16_units(units: usize) -> bool {
const MAX_UNITS: usize = 4096;
if units == 0 {
return true;
}
let units = units.min(MAX_UNITS);
let mut seq = KeySequence::new();
(0..units).all(|_| KeySequence::tap(VK_LEFT_KEY))
&& seq.down(VK_SHIFT_KEY)
&& (0..units).all(|_| KeySequence::tap(VK_RIGHT_KEY))
}
fn input_struct_size_i32() -> Option<i32> {
i32::try_from(std::mem::size_of::<INPUT>()).ok()
}
fn send_key(vk: VIRTUAL_KEY, key_up: bool) -> bool {
let input = INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 {
ki: KEYBDINPUT {
wVk: vk,
wScan: 0,
dwFlags: if key_up {
KEYEVENTF_KEYUP
} else {
KEYBD_EVENT_FLAGS::default()
},
time: 0,
dwExtraInfo: 0,
},
},
};
let Some(input_size) = input_struct_size_i32() else {
return false;
};
let sent = unsafe { SendInput(&[input], input_size) };
usize::try_from(sent).is_ok_and(|n| n != 0)
}