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

In macOS, mouse movement interrupts the press of the meta key. #201

Open
yetone opened this issue Sep 8, 2023 · 2 comments
Open

In macOS, mouse movement interrupts the press of the meta key. #201

yetone opened this issue Sep 8, 2023 · 2 comments

Comments

@yetone
Copy link

yetone commented Sep 8, 2023

Describe the bug
I use enigo to simulate pressing cmd+a on macOS to trigger the select all operation in the input box. It works fine when I don't move the mouse, but as soon as I move the mouse, only one 'a' character is outputted in the input box. It seems that moving the mouse interrupts the press of the cmd key.

To Reproduce

use enigo::*;

fn main() {
    ::std::thread::sleep(::std::time::Duration::from_secs(3));
    println!("trigger cmd+a");
    let mut enigo = Enigo::new();
    enigo.key_down(Key::Command);
    enigo.key_click(Key::Layout('a'));
    enigo.key_up(Key::Command);
    println!("done!");
}
Area.mp4

Expected behavior
Trigger the cmd+a shortcut to select all text in the input box.

Environment (please complete the following information):

  • OS: macOS
  • Rust 1.74.0-nightly (58e967a9c 2023-09-03)
  • Library Version enigo 0.0.13

Additional context
None

@pentamassiv
Copy link
Collaborator

pentamassiv commented Sep 26, 2023

Thank you for your report. I reproduced it successfully on the mac I use too. I look into it

It also happens for me, if the raw keycode (Key::Raw(0x37)) for Command is sent. It is not only a mouse move but can also be a key press. Other modifiers such as Shift are also affected. This most likely has to do with the queue of the events and #105. On macOS Enigo sleeps at a couple of places in the code because otherwise some of the sent events would get ignored. Only the modifiers appear to be affected though. My guess is that if you use the mouse or enter a key, another event is generated. Enigo sleeps, but the event from the actual hardware will cause the modifier event to be ignored. It would be nice if we could solve this issue once and for all and get rid of the sleep.

@xitanggg
Copy link

xitanggg commented Jul 5, 2024

I also run into this issue, and agree that it would be nice to get rid of sleep. Unfortunately, Apple's Core Graphics documentation isn't very clear about the details/implements, which makes it challenging to understand its behavior.

For this particular problem, the comment at #105 (comment) shares a workaround solution by using event flags in Core Graphics to set flag keys such as Shift or Command, and event created this way won't be affected by mouse movement and other key press. A example snippet is shown below:

use core_graphics::{
    event::{ CGEvent, CGEventTapLocation, KeyCode, CGEventFlags },
    event_source::{ CGEventSource, CGEventSourceStateID },
};

// Define CG key code for "a" key
// Reference: https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX10.13.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h#L197
static A_KEY_CODE: u16 = 0x00;

fn click_cmd_a() {
    // Implementation reference: https://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events

    // Event source state id reference: https://developer.apple.com/documentation/coregraphics/cgeventsourcestateid
    let event_source_state_id = CGEventSourceStateID::CombinedSessionState;
    let event_source = CGEventSource::new(event_source_state_id).unwrap();
    // Event tap location reference: https://developer.apple.com/documentation/coregraphics/cgeventtaplocation
    let event_tap_location = CGEventTapLocation::HID;

    let press_cmd_a_event = CGEvent::new_keyboard_event(
        event_source.clone(),
        A_KEY_CODE,
        true
    ).unwrap();
    press_cmd_a_event.set_flags(CGEventFlags::CGEventFlagCommand); // Set flags to Cmd
    press_cmd_a_event.post(event_tap_location);

    let release_a_event = CGEvent::new_keyboard_event(
        event_source.clone(),
        A_KEY_CODE,
        false
    ).unwrap();
    release_a_event.set_flags(CGEventFlags::CGEventFlagNull); // Reset flags to null
    release_a_event.post(event_tap_location);

    // Release Cmd Key for completeness. May or may not be necessary
    // given Apple's documentation is not clear on this.
    let release_cmd_event = CGEvent::new_keyboard_event(
        event_source.clone(),
        KeyCode::COMMAND,
        false
    ).unwrap();
    release_cmd_event.post(event_tap_location);
}

A potential future solution might be to leverage set_flags in engio say with a enigo.keys method that accepts multiple keys and maps keys to flags?

enigo.keys([Key::Command, Key::Unicode('a')], Click).unwrap();

This would likely require more thinkings to refine the solution and apis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants