From 3c267eab9b1f6b7760d188b75492dd5735cc96d6 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 18:32:44 +0200 Subject: [PATCH 01/15] Fixed entering new lines on Windows --- examples/keyboard.rs | 8 +++----- src/win/win_impl.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/examples/keyboard.rs b/examples/keyboard.rs index 5e6738b8..65b8ce06 100644 --- a/examples/keyboard.rs +++ b/examples/keyboard.rs @@ -9,14 +9,12 @@ fn main() { env_logger::init(); thread::sleep(Duration::from_secs(2)); let mut enigo = Enigo::new(&Settings::default()).unwrap(); - // write text enigo - .text("Hello World! here is a lot of text ❤️") + // .text("Test with lots of newlines") + .text("Test\nwith \nlots \nof \nnewlines") .unwrap(); - // select all - enigo.key(Key::Control, Press).unwrap(); enigo.key(Key::Unicode('a'), Click).unwrap(); - enigo.key(Key::Control, Release).unwrap(); + //enigo.key(Key::Unicode('🔥'), Click).unwrap(); } diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index ce2bb8f3..11bc058b 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -35,6 +35,9 @@ pub struct Enigo { } fn send_input(input: &[INPUT]) -> InputResult<()> { + if input.len() == 0 { + return Ok(()); + } let Ok(input_size): Result = size_of::().try_into() else { return Err(InputError::InvalidInput( "the size of the INPUT was so large, the size exceeded i32::MAX", @@ -240,15 +243,32 @@ impl Keyboard for Enigo { } 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? + '\n' => { + send_input(&input)?; + input.clear(); + self.key(Key::Return, Direction::Click)? + } + '\r' => { + /* + send_input(&input)?; + input.clear(); + self.key(Key::, Direction::Click)? // TODO: What is the correct key to type here? + */ + } + '\t' => { + send_input(&input)?; + input.clear(); + self.key(Key::Tab, Direction::Click)? + } + '\0' => { + send_input(&input)?; + input.clear(); + return Err(InputError::InvalidInput("the text contained a null byte")); } - '\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 From 2ba2f5d0b4bbb997a29da7df213c5b156c96fea7 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 19:00:02 +0200 Subject: [PATCH 02/15] remove obsolete import from test --- src/win/win_impl.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 11bc058b..99408d7f 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -662,8 +662,6 @@ mod test { VK_ZOOM, }; - use super::Enigo; - let known_ordinary_keys = [ VK__none_, // 255 VK_0, // 48 From 4cf86ea883d2f66f35ec6f0c0748492b3b00bc39 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 21:24:53 +0200 Subject: [PATCH 03/15] moved sending the key to its own method so it can get used by the text function --- src/win/win_impl.rs | 170 +++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 99408d7f..d5eed0da 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -306,89 +306,7 @@ impl Keyboard for Enigo { /// 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![]; - - 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, - )); - } - }; - send_input(&input)?; - - match direction { - Direction::Press => { - debug!("added the key {key:?} to the held keys"); - self.held.0.push(key); - // TODO: Make it work that they can get released with the raw - // function as well - } - Direction::Release => { - debug!("removed the key {key:?} from the held keys"); - self.held.0.retain(|&k| k != key); - // TODO: Make it work that they can get released with the raw - // function as well - } - Direction::Click => (), - } - - Ok(()) + self.send_key(key, direction) } fn raw(&mut self, scan: u16, direction: Direction) -> InputResult<()> { @@ -525,6 +443,92 @@ impl Enigo { } } + fn send_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![]; + + 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, + )); + } + }; + send_input(&input)?; + + match direction { + Direction::Press => { + debug!("added the key {key:?} to the held keys"); + self.held.0.push(key); + // TODO: Make it work that they can get released with the raw + // function as well + } + Direction::Release => { + debug!("removed the key {key:?} from the held keys"); + self.held.0.retain(|&k| k != key); + // TODO: Make it work that they can get released with the raw + // function as well + } + Direction::Click => (), + } + + Ok(()) + } + /// Returns a list of all currently pressed keys pub fn held(&mut self) -> (Vec, Vec) { self.held.clone() From 00080e606324dee91e45a50e17ba242a052f5511 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 21:29:52 +0200 Subject: [PATCH 04/15] Moved part of the function back to key() so that the queue only gets sent if that is used --- src/win/win_impl.rs | 59 +++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index d5eed0da..295f075c 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -306,7 +306,29 @@ impl Keyboard for Enigo { /// Sends a key event to the X11 server via `XTest` extension fn key(&mut self, key: Key, direction: Direction) -> InputResult<()> { - self.send_key(key, direction) + debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m"); + let mut input = vec![]; + + self.send_key(key, direction, &mut input)?; + send_input(&input)?; + + match direction { + Direction::Press => { + debug!("added the key {key:?} to the held keys"); + self.held.0.push(key); + // TODO: Make it work that they can get released with the raw + // function as well + } + Direction::Release => { + debug!("removed the key {key:?} from the held keys"); + self.held.0.retain(|&k| k != key); + // TODO: Make it work that they can get released with the raw + // function as well + } + Direction::Click => (), + } + + Ok(()) } fn raw(&mut self, scan: u16, direction: Direction) -> InputResult<()> { @@ -443,10 +465,13 @@ impl Enigo { } } - fn send_key(&mut self, key: Key, direction: Direction) -> InputResult<()> { - debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m"); + fn send_key( + &mut self, + key: Key, + direction: Direction, + input_queue: &mut Vec, + ) -> InputResult<()> { let layout = Enigo::get_keyboard_layout(); - let mut input = vec![]; if let Key::Unicode(c) = key { // Handle special characters separately @@ -464,7 +489,7 @@ impl Enigo { 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( + input_queue.push(keybd_event( // No need to check if it is an extended key because we only enter unicode // chars here KEYEVENTF_SCANCODE, @@ -476,7 +501,7 @@ impl Enigo { } if direction == Direction::Click || direction == Direction::Release { for &(vk, scan) in &vk_and_scan_codes { - input.push(keybd_event( + input_queue.push(keybd_event( // No need to check if it is an extended key because we only enter unicode // chars here KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, @@ -497,10 +522,10 @@ impl Enigo { keyflags |= KEYEVENTF_EXTENDEDKEY; } if direction == Direction::Click || direction == Direction::Press { - input.push(keybd_event(keyflags, vk, scan, self.dw_extra_info)); + input_queue.push(keybd_event(keyflags, vk, scan, self.dw_extra_info)); } if direction == Direction::Click || direction == Direction::Release { - input.push(keybd_event( + input_queue.push(keybd_event( keyflags | KEYEVENTF_KEYUP, vk, scan, @@ -508,24 +533,6 @@ impl Enigo { )); } }; - send_input(&input)?; - - match direction { - Direction::Press => { - debug!("added the key {key:?} to the held keys"); - self.held.0.push(key); - // TODO: Make it work that they can get released with the raw - // function as well - } - Direction::Release => { - debug!("removed the key {key:?} from the held keys"); - self.held.0.retain(|&k| k != key); - // TODO: Make it work that they can get released with the raw - // function as well - } - Direction::Click => (), - } - Ok(()) } From 330e30320acdd1084fbc05471419dbe67e9b73b1 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 21:33:28 +0200 Subject: [PATCH 05/15] The input queue can only be 2 or less when simulating key() --- src/win/win_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 295f075c..a77510ec 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -307,7 +307,7 @@ impl Keyboard for Enigo { /// 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 mut input = vec![]; + let mut input = Vec::with_capacity(2); self.send_key(key, direction, &mut input)?; send_input(&input)?; From 7d79d687d09774b7de8d1b3ee2df10d638650761 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 21:38:25 +0200 Subject: [PATCH 06/15] Use the send_key function in the text() function --- src/win/win_impl.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index a77510ec..58c40f9d 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -35,6 +35,7 @@ pub struct Enigo { } fn send_input(input: &[INPUT]) -> InputResult<()> { + println!("SEND INPUT"); if input.len() == 0 { return Ok(()); } @@ -247,26 +248,15 @@ impl Keyboard for Enigo { for c in text.chars() { // Handle special characters separately match c { - '\n' => { - send_input(&input)?; - input.clear(); - self.key(Key::Return, Direction::Click)? - } + '\n' => self.send_key(Key::Return, Direction::Click, &mut input)?, '\r' => { /* - send_input(&input)?; - input.clear(); - self.key(Key::, Direction::Click)? // TODO: What is the correct key to type here? + + self.send_key(Key::, Direction::Click, &mut input)? // TODO: What is the correct key to type here? */ } - '\t' => { - send_input(&input)?; - input.clear(); - self.key(Key::Tab, Direction::Click)? - } + '\t' => self.send_key(Key::Tab, Direction::Click, &mut input)?, '\0' => { - send_input(&input)?; - input.clear(); return Err(InputError::InvalidInput("the text contained a null byte")); } _ => (), @@ -476,10 +466,10 @@ impl Enigo { if let Key::Unicode(c) = key { // Handle special characters separately match c { - '\n' => return self.key(Key::Return, direction), + '\n' => self.send_key(Key::Return, direction, input_queue)?, '\r' => { // TODO: What is the correct key to type here? } - '\t' => return self.key(Key::Tab, direction), + '\t' => self.send_key(Key::Tab, direction, input_queue)?, '\0' => { debug!("entering Key::Unicode('\\0') is a noop"); return Ok(()); From 37a9101aab782ac22ffef7bfc090d5cce575c44c Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 22:49:11 +0200 Subject: [PATCH 07/15] Renamed function --- src/win/win_impl.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 58c40f9d..c25ebfeb 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -248,14 +248,14 @@ impl Keyboard for Enigo { for c in text.chars() { // Handle special characters separately match c { - '\n' => self.send_key(Key::Return, Direction::Click, &mut input)?, + '\n' => self.push_input_queue(Key::Return, Direction::Click, &mut input)?, '\r' => { /* self.send_key(Key::, Direction::Click, &mut input)? // TODO: What is the correct key to type here? */ } - '\t' => self.send_key(Key::Tab, Direction::Click, &mut input)?, + '\t' => self.push_input_queue(Key::Tab, Direction::Click, &mut input)?, '\0' => { return Err(InputError::InvalidInput("the text contained a null byte")); } @@ -299,7 +299,7 @@ impl Keyboard for Enigo { debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m"); let mut input = Vec::with_capacity(2); - self.send_key(key, direction, &mut input)?; + self.push_input_queue(key, direction, &mut input)?; send_input(&input)?; match direction { @@ -402,7 +402,7 @@ impl Enigo { fn get_vk_and_scan_codes(c: char, layout: HKL) -> InputResult> { let mut buffer = [0; 2]; // A buffer of length 2 is large enough to encode any char let utf16_surrogates: Vec = c.encode_utf16(&mut buffer).into(); - let mut results = vec![]; + let mut results = Vec::with_capacity(2); 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 @@ -455,7 +455,7 @@ impl Enigo { } } - fn send_key( + fn push_input_queue( &mut self, key: Key, direction: Direction, @@ -466,10 +466,10 @@ impl Enigo { if let Key::Unicode(c) = key { // Handle special characters separately match c { - '\n' => self.send_key(Key::Return, direction, input_queue)?, + '\n' => self.push_input_queue(Key::Return, direction, input_queue)?, '\r' => { // TODO: What is the correct key to type here? } - '\t' => self.send_key(Key::Tab, direction, input_queue)?, + '\t' => self.push_input_queue(Key::Tab, direction, input_queue)?, '\0' => { debug!("entering Key::Unicode('\\0') is a noop"); return Ok(()); From 60a0ccca9075dc07ea695aa791acfd5ad9c57951 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 23:05:44 +0200 Subject: [PATCH 08/15] moved keyflags around --- src/win/win_impl.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index c25ebfeb..90b18f24 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -462,8 +462,11 @@ impl Enigo { input_queue: &mut Vec, ) -> InputResult<()> { let layout = Enigo::get_keyboard_layout(); + let mut keyflags = KEYBD_EVENT_FLAGS::default(); if let Key::Unicode(c) = key { + keyflags |= KEYEVENTF_SCANCODE; + // Handle special characters separately match c { '\n' => self.push_input_queue(Key::Return, direction, input_queue)?, @@ -482,7 +485,7 @@ impl Enigo { input_queue.push(keybd_event( // No need to check if it is an extended key because we only enter unicode // chars here - KEYEVENTF_SCANCODE, + keyflags, vk, scan, self.dw_extra_info, @@ -494,7 +497,7 @@ impl Enigo { input_queue.push(keybd_event( // No need to check if it is an extended key because we only enter unicode // chars here - KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, + keyflags | KEYEVENTF_KEYUP, vk, scan, self.dw_extra_info, @@ -506,7 +509,6 @@ impl Enigo { // 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; From a8f023c1020f4610ccd36b3ec834282a07157fab Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 23:08:56 +0200 Subject: [PATCH 09/15] Return after adding the input to queue for the special chars --- src/win/win_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 90b18f24..a8dc338b 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -469,10 +469,10 @@ impl Enigo { // Handle special characters separately match c { - '\n' => self.push_input_queue(Key::Return, direction, input_queue)?, + '\n' => return self.push_input_queue(Key::Return, direction, input_queue), '\r' => { // TODO: What is the correct key to type here? } - '\t' => self.push_input_queue(Key::Tab, direction, input_queue)?, + '\t' => return self.push_input_queue(Key::Tab, direction, input_queue), '\0' => { debug!("entering Key::Unicode('\\0') is a noop"); return Ok(()); From 871c07d8b51b6aff8bc844521ef8cd67e38fa1dc Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 23:31:53 +0200 Subject: [PATCH 10/15] Simplified push_input_queue() --- examples/keyboard.rs | 5 +++-- src/win/win_impl.rs | 23 +++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/keyboard.rs b/examples/keyboard.rs index 65b8ce06..08704df5 100644 --- a/examples/keyboard.rs +++ b/examples/keyboard.rs @@ -7,7 +7,7 @@ use std::time::Duration; fn main() { env_logger::init(); - thread::sleep(Duration::from_secs(2)); + thread::sleep(Duration::from_secs(1)); let mut enigo = Enigo::new(&Settings::default()).unwrap(); // write text enigo @@ -16,5 +16,6 @@ fn main() { .unwrap(); enigo.key(Key::Unicode('a'), Click).unwrap(); - //enigo.key(Key::Unicode('🔥'), Click).unwrap(); + enigo.key(Key::Return, Click).unwrap(); + enigo.key(Key::Unicode('🔥'), Click).unwrap(); } diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index a8dc338b..fc909920 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -399,7 +399,7 @@ impl Enigo { unsafe { GetKeyboardLayout(current_window_thread_id) } } - fn get_vk_and_scan_codes(c: char, layout: HKL) -> InputResult> { + fn char_to_virtual_keys(c: char, layout: HKL) -> InputResult> { let mut buffer = [0; 2]; // A buffer of length 2 is large enough to encode any char let utf16_surrogates: Vec = c.encode_utf16(&mut buffer).into(); let mut results = Vec::with_capacity(2); @@ -416,8 +416,7 @@ impl Enigo { 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 scan_code = Enigo::get_scancode(virtual_key, layout)?; - results.push((virtual_key, scan_code)); + results.push(virtual_key); } Ok(results) } @@ -464,7 +463,7 @@ impl Enigo { let layout = Enigo::get_keyboard_layout(); let mut keyflags = KEYBD_EVENT_FLAGS::default(); - if let Key::Unicode(c) = key { + let virtual_keys = if let Key::Unicode(c) = key { keyflags |= KEYEVENTF_SCANCODE; // Handle special characters separately @@ -479,7 +478,9 @@ impl Enigo { } _ => (), } - let vk_and_scan_codes = Enigo::get_vk_and_scan_codes(c, layout)?; + Enigo::char_to_virtual_keys(c, layout)? + + /* if direction == Direction::Click || direction == Direction::Press { for &(vk, scan) in &vk_and_scan_codes { input_queue.push(keybd_event( @@ -503,16 +504,21 @@ impl Enigo { 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(); + vec![VIRTUAL_KEY::try_from(key).unwrap()] + }; + + for vk in virtual_keys { let scan = Enigo::get_scancode(vk, layout)?; + // 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)); } @@ -524,7 +530,8 @@ impl Enigo { self.dw_extra_info, )); } - }; + } + Ok(()) } From 66bc7d75bad4dd51f1c6e167c15a19771230c385 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Oct 2024 23:40:13 +0200 Subject: [PATCH 11/15] Reordered input args and added simulate_as_unicode parameter --- src/win/win_impl.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index fc909920..5bd0707f 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -248,14 +248,14 @@ impl Keyboard for Enigo { for c in text.chars() { // Handle special characters separately match c { - '\n' => self.push_input_queue(Key::Return, Direction::Click, &mut input)?, + '\n' => self.push_input_queue(&mut input, Key::Return, false, Direction::Click)?, '\r' => { /* - self.send_key(Key::, Direction::Click, &mut input)? // TODO: What is the correct key to type here? + self.send_key(&mut input, Key::, false, Direction::Click)? // TODO: What is the correct key to type here? */ } - '\t' => self.push_input_queue(Key::Tab, Direction::Click, &mut input)?, + '\t' => self.push_input_queue(&mut input, Key::Tab, false, Direction::Click)?, '\0' => { return Err(InputError::InvalidInput("the text contained a null byte")); } @@ -299,7 +299,7 @@ impl Keyboard for Enigo { debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m"); let mut input = Vec::with_capacity(2); - self.push_input_queue(key, direction, &mut input)?; + self.push_input_queue(&mut input, key, false, direction)?; send_input(&input)?; match direction { @@ -456,9 +456,10 @@ impl Enigo { fn push_input_queue( &mut self, + input_queue: &mut Vec, key: Key, + simulate_as_unicode: bool, direction: Direction, - input_queue: &mut Vec, ) -> InputResult<()> { let layout = Enigo::get_keyboard_layout(); let mut keyflags = KEYBD_EVENT_FLAGS::default(); @@ -468,10 +469,10 @@ impl Enigo { // Handle special characters separately match c { - '\n' => return self.push_input_queue(Key::Return, direction, input_queue), + '\n' => return self.push_input_queue(input_queue, Key::Return, false, direction), '\r' => { // TODO: What is the correct key to type here? } - '\t' => return self.push_input_queue(Key::Tab, direction, input_queue), + '\t' => return self.push_input_queue(input_queue, Key::Tab, false, direction), '\0' => { debug!("entering Key::Unicode('\\0') is a noop"); return Ok(()); From e5f62f8e6bf311eaa38e649def22fe668a631bd0 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Mon, 7 Oct 2024 01:12:42 +0200 Subject: [PATCH 12/15] Refactored code --- examples/keyboard.rs | 4 +- src/win/win_impl.rs | 123 ++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 75 deletions(-) diff --git a/examples/keyboard.rs b/examples/keyboard.rs index 08704df5..22e29445 100644 --- a/examples/keyboard.rs +++ b/examples/keyboard.rs @@ -12,10 +12,10 @@ fn main() { // write text enigo // .text("Test with lots of newlines") - .text("Test\nwith \nlots \nof \nnewlines") + .text("Test\nwith \nlots \nof \nnewlines🔥") .unwrap(); enigo.key(Key::Unicode('a'), Click).unwrap(); enigo.key(Key::Return, Click).unwrap(); - enigo.key(Key::Unicode('🔥'), Click).unwrap(); + // enigo.key(Key::Unicode('🔥'), Click).unwrap(); } diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index 5bd0707f..e0e6c3a6 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -32,6 +32,7 @@ pub struct Enigo { held: (Vec, Vec), // 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<()> { @@ -242,54 +243,10 @@ impl Keyboard for Enigo { if text.is_empty() { return Ok(()); // Nothing to simulate. } - let mut buffer = [0; 2]; 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' => self.push_input_queue(&mut input, Key::Return, false, Direction::Click)?, - '\r' => { - /* - - self.send_key(&mut input, Key::, false, Direction::Click)? // TODO: What is the correct key to type here? - */ - } - '\t' => self.push_input_queue(&mut input, Key::Tab, false, 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) } @@ -299,7 +256,7 @@ impl Keyboard for Enigo { debug!("\x1b[93mkey(key: {key:?}, direction: {direction:?})\x1b[0m"); let mut input = Vec::with_capacity(2); - self.push_input_queue(&mut input, key, false, direction)?; + self.add_key_to_queue(&mut input, key, false, direction)?; send_input(&input)?; match direction { @@ -383,6 +340,7 @@ impl Enigo { } = settings; let held = (vec![], vec![]); + let utf16_encoding_buffer = [0; 2]; debug!("\x1b[93mconnection established on windows\x1b[0m"); @@ -390,6 +348,7 @@ impl Enigo { 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, }) } @@ -399,24 +358,31 @@ impl Enigo { unsafe { GetKeyboardLayout(current_window_thread_id) } } - fn char_to_virtual_keys(c: char, layout: HKL) -> InputResult> { - let mut buffer = [0; 2]; // A buffer of length 2 is large enough to encode any char - let utf16_surrogates: Vec = c.encode_utf16(&mut buffer).into(); + fn surrogates_to_vk_scans( + simulate_as_unicode: bool, + utf16_surrogates: &mut [u16], + ) -> InputResult> { let mut results = Vec::with_capacity(2); - 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())); + 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); - results.push(virtual_key); } Ok(results) } @@ -454,32 +420,39 @@ impl Enigo { } } - fn push_input_queue( + fn add_key_to_queue( &mut self, input_queue: &mut Vec, key: Key, - simulate_as_unicode: bool, + simulate_as_unicode: bool, // If it is a char, enter it as a KEYEVENTF_UNICODE direction: Direction, ) -> InputResult<()> { - let layout = Enigo::get_keyboard_layout(); let mut keyflags = KEYBD_EVENT_FLAGS::default(); let virtual_keys = if let Key::Unicode(c) = key { - keyflags |= KEYEVENTF_SCANCODE; - // Handle special characters separately match c { - '\n' => return self.push_input_queue(input_queue, Key::Return, false, direction), + '\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.push_input_queue(input_queue, Key::Tab, false, direction), + '\t' => return self.add_key_to_queue(input_queue, Key::Tab, false, direction), '\0' => { debug!("entering Key::Unicode('\\0') is a noop"); return Ok(()); } _ => (), } - Enigo::char_to_virtual_keys(c, layout)? + + 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 { @@ -509,12 +482,16 @@ impl Enigo { } 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 - vec![VIRTUAL_KEY::try_from(key).unwrap()] - }; - for vk in virtual_keys { + 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; From 460d0be6110b0357d1eaf184b80b1afe3ff7e67a Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Mon, 7 Oct 2024 01:19:22 +0200 Subject: [PATCH 13/15] Added to changelog --- CHANGES.md | 1 + examples/keyboard.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9427ef0c..0747ea5b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/examples/keyboard.rs b/examples/keyboard.rs index 22e29445..5e6738b8 100644 --- a/examples/keyboard.rs +++ b/examples/keyboard.rs @@ -7,15 +7,16 @@ use std::time::Duration; fn main() { env_logger::init(); - thread::sleep(Duration::from_secs(1)); + thread::sleep(Duration::from_secs(2)); let mut enigo = Enigo::new(&Settings::default()).unwrap(); + // write text enigo - // .text("Test with lots of newlines") - .text("Test\nwith \nlots \nof \nnewlines🔥") + .text("Hello World! here is a lot of text ❤️") .unwrap(); + // select all + enigo.key(Key::Control, Press).unwrap(); enigo.key(Key::Unicode('a'), Click).unwrap(); - enigo.key(Key::Return, Click).unwrap(); - // enigo.key(Key::Unicode('🔥'), Click).unwrap(); + enigo.key(Key::Control, Release).unwrap(); } From b45a8802b2060fb7da5ff8207aef7b0f65e97ebe Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Mon, 7 Oct 2024 01:20:57 +0200 Subject: [PATCH 14/15] . --- src/win/win_impl.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index e0e6c3a6..b0196f1a 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -36,7 +36,6 @@ pub struct Enigo { } fn send_input(input: &[INPUT]) -> InputResult<()> { - println!("SEND INPUT"); if input.len() == 0 { return Ok(()); } From cc77a3fc1e85384722c61dbc67d892cf83387f30 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Mon, 7 Oct 2024 01:24:42 +0200 Subject: [PATCH 15/15] Clippy --- src/win/win_impl.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index b0196f1a..ee6eddc9 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -32,7 +32,8 @@ pub struct Enigo { held: (Vec, Vec), // 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 + 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<()> { @@ -448,7 +449,8 @@ impl Enigo { keyflags |= KEYEVENTF_SCANCODE; } - // Windows uses uft-16 encoding. With UTF-16, a character can be encoded as one or two u16 + // 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)? @@ -491,7 +493,8 @@ impl Enigo { }; for (vk, scan) in virtual_keys { - // TODO: Get rid of this check for Key::Unicode because none of them can be an extended key + // 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; }