Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Fix being unable to simulate text with multiple newline chars #339

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- win: Respect the language of the current window to determine which scancodes to send
- win: Send the virtual key and its scan code in the events to work with programs that only look at one of the two
- macOS: Moving the mouse no longer breaks simulating input
- win: Fix being unable to enter text containing multiple newline chars

# 0.2.1
## Changed
Expand Down
252 changes: 130 additions & 122 deletions src/win/win_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ pub struct Enigo {
held: (Vec<Key>, Vec<ScanCode>), // Currently held keys
release_keys_when_dropped: bool,
dw_extra_info: usize,
utf16_encoding_buffer: [u16; 2], /* A buffer of length 2 is large enough to encode any char
* in utf16 */
}

fn send_input(input: &[INPUT]) -> InputResult<()> {
if input.len() == 0 {
return Ok(());
}
let Ok(input_size): Result<i32, _> = size_of::<INPUT>().try_into() else {
return Err(InputError::InvalidInput(
"the size of the INPUT was so large, the size exceeded i32::MAX",
Expand Down Expand Up @@ -238,118 +243,20 @@ impl Keyboard for Enigo {
if text.is_empty() {
return Ok(()); // Nothing to simulate.
}
let mut buffer = [0; 2];

let mut input = vec![];
let mut input = Vec::with_capacity(2 * text.len()); // Each char needs one event to press and one to release it
for c in text.chars() {
// Handle special characters separately
match c {
'\n' => return self.key(Key::Return, Direction::Click),
'\r' => { // TODO: What is the correct key to type here?
}
'\t' => return self.key(Key::Tab, Direction::Click),
'\0' => return Err(InputError::InvalidInput("the text contained a null byte")),
_ => (),
}
// Windows uses uft-16 encoding. We need to check
// for variable length characters. As such some
// characters can be 32 bit long and those are
// encoded in what is called high and low surrogates.
// Each are 16 bit wide and need to be sent after
// another to the SendInput function
let result = c.encode_utf16(&mut buffer);
for &utf16_surrogate in &*result {
input.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
KEYEVENTF_UNICODE,
// Must be zero
VIRTUAL_KEY(0),
utf16_surrogate,
self.dw_extra_info,
));
input.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
// Must be zero
VIRTUAL_KEY(0),
// TODO: Double check if this could also be utf16_surrogate (I think it doesn't
// make a difference)
result[0],
self.dw_extra_info,
));
}
self.add_key_to_queue(&mut input, Key::Unicode(c), true, Direction::Click)?;
}
send_input(&input)
}

/// Sends a key event to the X11 server via `XTest` extension
fn key(&mut self, key: Key, direction: Direction) -> InputResult<()> {
debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m");
let layout = Enigo::get_keyboard_layout();
let mut input = vec![];
let mut input = Vec::with_capacity(2);

if let Key::Unicode(c) = key {
// Handle special characters separately
match c {
'\n' => return self.key(Key::Return, direction),
'\r' => { // TODO: What is the correct key to type here?
}
'\t' => return self.key(Key::Tab, direction),
'\0' => {
debug!("entering Key::Unicode('\\0') is a noop");
return Ok(());
}
_ => (),
}
let vk_and_scan_codes = Enigo::get_vk_and_scan_codes(c, layout)?;
if direction == Direction::Click || direction == Direction::Press {
for &(vk, scan) in &vk_and_scan_codes {
input.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
KEYEVENTF_SCANCODE,
vk,
scan,
self.dw_extra_info,
));
}
}
if direction == Direction::Click || direction == Direction::Release {
for &(vk, scan) in &vk_and_scan_codes {
input.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP,
vk,
scan,
self.dw_extra_info,
));
}
}
} else {
// It is okay to unwrap here because key_to_keycode only returns a None for
// Key::Unicode and we already ensured that is not the case
let vk = VIRTUAL_KEY::try_from(key).unwrap();
let scan = Enigo::get_scancode(vk, layout)?;
let mut keyflags = KEYBD_EVENT_FLAGS::default();

if Enigo::is_extended_key(vk) {
keyflags |= KEYEVENTF_EXTENDEDKEY;
}
if direction == Direction::Click || direction == Direction::Press {
input.push(keybd_event(keyflags, vk, scan, self.dw_extra_info));
}
if direction == Direction::Click || direction == Direction::Release {
input.push(keybd_event(
keyflags | KEYEVENTF_KEYUP,
vk,
scan,
self.dw_extra_info,
));
}
};
self.add_key_to_queue(&mut input, key, false, direction)?;
send_input(&input)?;

match direction {
Expand Down Expand Up @@ -433,13 +340,15 @@ impl Enigo {
} = settings;

let held = (vec![], vec![]);
let utf16_encoding_buffer = [0; 2];

debug!("\x1b[93mconnection established on windows\x1b[0m");

Ok(Self {
held,
release_keys_when_dropped: *release_keys_when_dropped,
dw_extra_info: dw_extra_info.unwrap_or(crate::EVENT_MARKER as usize),
utf16_encoding_buffer,
})
}

Expand All @@ -449,25 +358,31 @@ impl Enigo {
unsafe { GetKeyboardLayout(current_window_thread_id) }
}

fn get_vk_and_scan_codes(c: char, layout: HKL) -> InputResult<Vec<(VIRTUAL_KEY, ScanCode)>> {
let mut buffer = [0; 2]; // A buffer of length 2 is large enough to encode any char
let utf16_surrogates: Vec<u16> = c.encode_utf16(&mut buffer).into();
let mut results = vec![];
for &utf16_surrogate in &utf16_surrogates {
// Translate a character to the corresponding virtual-key code and shift state.
// If the function succeeds, the low-order byte of the return value contains the
// virtual-key code and the high-order byte contains the shift state, which can
// be a combination of the following flag bits. If the function finds no key
// that translates to the passed character code, both the low-order and
// high-order bytes contain –1

let virtual_key = unsafe { VkKeyScanExW(utf16_surrogate, layout) };
if virtual_key < 0 {
return Err(InputError::Mapping("Could not translate the character to the corresponding virtual-key code and shift state for the current keyboard".to_string()));
fn surrogates_to_vk_scans(
simulate_as_unicode: bool,
utf16_surrogates: &mut [u16],
) -> InputResult<Vec<(VIRTUAL_KEY, ScanCode)>> {
let mut results = Vec::with_capacity(2);
for utf16_surrogate in utf16_surrogates {
if simulate_as_unicode {
results.push((VIRTUAL_KEY(0), *utf16_surrogate));
} else {
// Translate a character to the corresponding virtual-key code and shift state.
// If the function succeeds, the low-order byte of the return value contains the
// virtual-key code and the high-order byte contains the shift state, which can
// be a combination of the following flag bits. If the function finds no key
// that translates to the passed character code, both the low-order and
// high-order bytes contain –1
let layout = Enigo::get_keyboard_layout();
let virtual_key = unsafe { VkKeyScanExW(*utf16_surrogate, layout) };
if virtual_key < 0 {
return Err(InputError::Mapping("Could not translate the character to the corresponding virtual-key code and shift state for the current keyboard".to_string()));
}
let virtual_key = VIRTUAL_KEY(virtual_key as u16);
let scancode = Enigo::get_scancode(virtual_key, layout)?;

results.push((virtual_key, scancode));
}
let virtual_key = VIRTUAL_KEY(virtual_key as u16);
let scan_code = Enigo::get_scancode(virtual_key, layout)?;
results.push((virtual_key, scan_code));
}
Ok(results)
}
Expand Down Expand Up @@ -505,6 +420,101 @@ impl Enigo {
}
}

fn add_key_to_queue(
&mut self,
input_queue: &mut Vec<INPUT>,
key: Key,
simulate_as_unicode: bool, // If it is a char, enter it as a KEYEVENTF_UNICODE
direction: Direction,
) -> InputResult<()> {
let mut keyflags = KEYBD_EVENT_FLAGS::default();

let virtual_keys = if let Key::Unicode(c) = key {
// Handle special characters separately
match c {
'\n' => return self.add_key_to_queue(input_queue, Key::Return, false, direction),
'\r' => { // TODO: What is the correct key to type here?
}
'\t' => return self.add_key_to_queue(input_queue, Key::Tab, false, direction),
'\0' => {
debug!("entering Key::Unicode('\\0') is a noop");
return Ok(());
}
_ => (),
}

if simulate_as_unicode {
keyflags |= KEYEVENTF_UNICODE;
} else {
keyflags |= KEYEVENTF_SCANCODE;
}

// Windows uses uft-16 encoding. With UTF-16, a character can be encoded as one
// or two u16
let utf16_surrogates = c.encode_utf16(&mut self.utf16_encoding_buffer);

Enigo::surrogates_to_vk_scans(simulate_as_unicode, utf16_surrogates)?

/*
if direction == Direction::Click || direction == Direction::Press {
for &(vk, scan) in &vk_and_scan_codes {
input_queue.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
keyflags,
vk,
scan,
self.dw_extra_info,
));
}
}
if direction == Direction::Click || direction == Direction::Release {
for &(vk, scan) in &vk_and_scan_codes {
input_queue.push(keybd_event(
// No need to check if it is an extended key because we only enter unicode
// chars here
keyflags | KEYEVENTF_KEYUP,
vk,
scan,
self.dw_extra_info,
));
}
}*/
} else {
// It is okay to unwrap here because key_to_keycode only returns a None for
// Key::Unicode and we already ensured that is not the case

let layout = Enigo::get_keyboard_layout();

let vk = VIRTUAL_KEY::try_from(key).unwrap();
let scan = Enigo::get_scancode(vk, layout)?;

vec![(vk, scan)]
};

for (vk, scan) in virtual_keys {
// TODO: Get rid of this check for Key::Unicode because none of them can be an
// extended key
if Enigo::is_extended_key(vk) {
keyflags |= KEYEVENTF_EXTENDEDKEY;
}

if direction == Direction::Click || direction == Direction::Press {
input_queue.push(keybd_event(keyflags, vk, scan, self.dw_extra_info));
}
if direction == Direction::Click || direction == Direction::Release {
input_queue.push(keybd_event(
keyflags | KEYEVENTF_KEYUP,
vk,
scan,
self.dw_extra_info,
));
}
}

Ok(())
}

/// Returns a list of all currently pressed keys
pub fn held(&mut self) -> (Vec<Key>, Vec<ScanCode>) {
self.held.clone()
Expand Down Expand Up @@ -642,8 +652,6 @@ mod test {
VK_ZOOM,
};

use super::Enigo;

let known_ordinary_keys = [
VK__none_, // 255
VK_0, // 48
Expand Down