enigo-rs / enigo

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

Sending characer sequences is slow #38

Closed Davejkane closed 5 years ago

Davejkane commented 7 years ago

First off let me say that I'm using linux.

I'd like to use this library to control the keyboard programmatically while the user is typing. I really like the API, but sending a character sequence takes a long time, at least 40ms per character due to sleeping 10ms before and after every keydown and keyup, so a single word can easily take half a second to print.

I tried forking and changing those sleep values but if it's too short, not all of the characters print, so I'm guessing they're there for good reason.

I've been looking at plover, which is implemented in python. In plover, they also use XTest to send character sequences, but they render almost instantaneously. I can hammer keys as fast as I can and plover can easily keep up, turning single keystrokes into multi word sequences. See here https://github.com/openstenoproject/plover/blob/master/plover/oslayer/xkeyboardcontrol.py#L1223

They don't appear to be flushing after every keydown and keyup, but instead appear to be calling display.sync at the end of the key sequence. Is there any way enigo can print entire key sequences instantaneously or is there some limitation there?

pythoneer commented 7 years ago

To answer your last question, not currently. But thanks for the suggestion i try to investigate this and hopefully come to a solution for this problem that is itching for a long time in enigo – you're right that the sleeps are currently there for exactly that reason.

jD91mZM2 commented 7 years ago

Is the parser (without unicode) faster, by any chance? key_sequence supports all of unicode by temporarily assigning a button to it, which will lose a bunch of speed.

Davejkane commented 7 years ago
    let mut enigo = Enigo::new();

    // write text
    enigo.key_sequence_parse("one of the");

On my machine this is pretty much just as slow, even with cargo build --release. Not only that but it only prints "oneofthe". Don't know if that's because I need to send spaces separately. Either way it calls key_sequence underneath anyway, so it'll still have those 40ms per key click.

jD91mZM2 commented 7 years ago

It doesn't call key_sequence unless you specify unicode. The space problem is because it tries to press it without unicode support. Will fix that so it uses the Key::Space thing instead.

jD91mZM2 commented 7 years ago

3b40237

Davejkane commented 7 years ago

Cool thanks. I'm going to keep my eye on this one. Unfortunately I don't know enough C, Rust nor Python to help out, although I will tinker around and see if I can come up with anything. Out of interest, do you know if this slow typing problem is present on windows and macOS?

jD91mZM2 commented 7 years ago

I don't, sorry. I'll tell you what, though - I'll see if I can set up a profiler to see what takes the most time.

jD91mZM2 commented 7 years ago

Oh, by the way - Are you using the crates.io version or the git version of enigo? version = "0.0.11" or { git = "https://github.com/enigo-rs/enigo" }?

Davejkane commented 7 years ago

I am using the crates.io versioin "0.0.11'

Davejkane commented 7 years ago

But no matter what It's all going to boil down to this https://github.com/enigo-rs/enigo/blob/master/src/linux/linux_impl.rs#L387 right? so the 40ms sleeps per complete keypress are going to be there no matter how you do the input as far as I can tell

jD91mZM2 commented 7 years ago

Oh, we literally sleep 40 milliseconds... I never bothered to even check that 😛 Surprised there is one before and after instead of just... inbetween

EDIT: So the problem isn't actually the speed, it's the unrealiable-ness of it. If we manage to make every key count 100% of the time, sleeping wouldn't be necessary

Davejkane commented 7 years ago

I think so. I'm also wondering about XFlush. All the X libraries have terrible documentation, so I can't really tell what thats for, but perhaps only calling it once after registering all of the keypresses for the whole string would be enough?

jD91mZM2 commented 7 years ago

I tried that, didn't appear to work. I'll tell you what though, I just asked in the Discord server if we should start using libxdo instead of xtest. Because libxdo is literally xdotool, and that tool works with unicode. It would add that requirement, sadly. But... What do you think?

jD91mZM2 commented 6 years ago

It's not actually fixed because the delay is 12000 by default. Will be fixed once that becomes customizable.

mbilker commented 6 years ago

I just ran into this design choice.

I thought Rust's MPSC channels and crossbeam's MsQueue were being slow, so I took another look at the key_up and key_down functions and saw the 12,000 microsecond delay. I forked the code and set the delay to zero.

jD91mZM2 commented 6 years ago

@mbilker Less delay means it's less likely to correctly press unicode characters for some reason. That's why we have the default, and why making it customizable would be optimal

mbilker commented 6 years ago

@jD91mZM2 That makes sense. I am not using unicode characters and that makes sense for the delay. The delay could be customized through a builder interface or a parameter to a separate constructor (like with_delay(12000)) and the delay value would be stored as another value in the struct.

jD91mZM2 commented 6 years ago

True. The biggest problem with implementing a way to set the delay is that it would have to be multiplatform. And even if every platform does have a delay thing that would make sense to change (might do, might not, haven't bothered to check it), I can't do it because I don't own a Windows or Mac copy to test on.

pythoneer commented 5 years ago

I think this is temporarily fixed by https://github.com/enigo-rs/enigo/commit/098e331f666c7e836743f589b6af89d22a5c0d05

I do think that we should have a specific cross platform delay for inputs either way. Opened up a new issue for this here