enigo-rs / enigo

Cross platform input simulation in Rust
MIT License
1.08k stars 107 forks source link

Clear modifiers before typing #83

Open takistakis opened 4 years ago

takistakis commented 4 years ago

enigo (at least the linux implementation) does not ignore modifiers such as Meta. So when executing a program that calls enigo.key_sequence("asdf") with a keybinding Meta+a, if Meta is not unpressed quickly, the equivalent of "{+META}asdf" is typed instead.

The following diff, creates a clear_modifiers function for the linux implementation, which calls xdo_clear_active_modifiers.

How does enigo work in this regard in windows and mac implementations? If they don't have the same behavior, enigo could clear modifiers implicitly before typing on linux.

diff --git a/src/lib.rs b/src/lib.rs
index fe19ff1..e1408b0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -375,6 +375,9 @@ pub trait KeyboardControllable {
     /// [key_up](trait.KeyboardControllable.html#tymethod.key_up)
     /// function they're just invoked consecutively
     fn key_click(&mut self, key: Key);
+
+    /// Clears active modifiers such as Meta, Ctrl etc.
+    fn clear_modifiers(&mut self);
 }

 impl Enigo {
diff --git a/src/linux.rs b/src/linux.rs
index 40506df..6a53447 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -9,6 +9,7 @@ const CURRENT_WINDOW: c_int = 0;
 const DEFAULT_DELAY: u64 = 12000;
 type Window = c_int;
 type Xdo = *const c_void;
+type Charcodemap = *const c_void;

 #[link(name = "xdo")]
 extern "C" {
@@ -45,6 +46,18 @@ extern "C" {
         string: *const c_char,
         delay: useconds_t,
     ) -> c_int;
+
+    fn xdo_get_active_modifiers(
+        xdo: Xdo,
+        active_mods: *const Charcodemap,
+        active_mods_n: *const c_int,
+    ) -> c_int;
+    fn xdo_clear_active_modifiers(
+        xdo: Xdo,
+        window: Window,
+        active_mods: Charcodemap,
+        active_mods_n: c_int,
+    ) -> c_int;
 }

 fn mousebutton(button: MouseButton) -> c_int {
@@ -249,4 +262,13 @@ impl KeyboardControllable for Enigo {
             );
         }
     }
+
+    fn clear_modifiers(&mut self) {
+        unsafe {
+            let active_mods: Charcodemap = xdo_new(ptr::null());
+            let active_mods_n: c_int = 0;
+            xdo_get_active_modifiers(self.xdo, &active_mods, &active_mods_n);
+            xdo_clear_active_modifiers(self.xdo, CURRENT_WINDOW, active_mods, active_mods_n);
+        }
+    }
 }
pythoneer commented 4 years ago

Thanks for opening this issue. Unfortunately i don't really understand the Problem. What do you mean by

with a keybinding Meta+a

is there a global keybinding and you're pressing Meta right before the code executes manually? I just don't understand what is really causing the issue here.

takistakis commented 4 years ago

Yes it's a global keybinding that's configured from the window manager (i3 in my case). I have set up Meta+p to call an application that uses enigo and types something. This happens as soon as p is pressed, and because Meta is still pressed at this point, each character is typed with Meta enabled. So instead of actually typing the character, it triggers other keybindings that are set up by the window manager.

indianakernick commented 3 years ago

The macOS implementation would look like this:

impl Enigo {
    pub fn clear_modifiers(&mut self) {
        let event = CGEvent::new(self.event_source.clone()).unwrap();
        event.set_type(CGEventType::FlagsChanged);
        event.set_flags(CGEventFlags::CGEventFlagNull);
        event.post(CGEventTapLocation::HID);
    }
}

I guess another alternative would be to put event.set_flags(CGEventFlags::CGEventFlagNull) inside of key_sequence.

pentamassiv commented 1 year ago

xdotool has a flag to clear modifiers and press them after the command was executed. From it's man page:

CLEARMODIFIERS
       Any command taking the --clearmodifiers flag will attempt to clear any active
       input modifiers during the command and restore them afterwards.

       For example, if you were to run this command:
        xdotool key a

       The result would be 'a' or 'A' depending on whether or not you were holding the
       shift key on your keyboard. Often it is undesirable to have any modifiers active,
       so you can tell xdotool to clear any active modifiers.

       The order of operations if you hold shift while running 'xdotool key
       --clearmodifiers a' is this:

       1. Query for all active modifiers (finds shift, in this case)
       2. Try to clear shift by sending 'key up' for the shift key
       3. Runs normal 'xdotool key a'
       4. Restore shift key by sending 'key down' for shift

Would the suggested macOS implementation restore the modifiers afterwards?