-
Notifications
You must be signed in to change notification settings - Fork 112
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
Making multi-keystroke macros more reliable? #150
Comments
It would be something if you could actually confirm that hypothesis... the code you're looking for is in Otherwise that sounds very strange behavior indeed. |
I've tried putting many instances of
I use this somewhat lengthy macro in Firefox to test:
When this works it seems to type at about the speed of someone typing at maybe 500 wpm. But it only works properly about 50% of the time. |
And what does the console say... does it log all the keys there despite them not seeming to get all the way to Firefox?
Can you show me where exactly you're adding the delays? |
Do you have an isuse for that? That sounds strange - perhaps something is weird with your system... I'd try to figure out the simpel things first before delving into key delay issues. :) |
The crash was probably down to some misuse of python syntax, like using I've seen the same results on multiple different computers and multiple unrelated Linux distros over the course of the last couple of years. I don't think it has anything to do with my system. Do you not have any issues with macros at all? Or have you not run the kind of test I ran that started this thread? If you can do the same sort of test with perfect results 100% of the time, then maybe this is actually a problem with the specific config from Kinto.sh or the patched branch of The view in the terminal when I remove As for where I tried to put the |
I'm new here. Haven't used the software much trying to decide if it's right for me and if I want to step up as part of a new small team to fork and continue development, respond and fix issues like these, etc. :-)
Possibly but his changes to output have more to do with modifier states than plain keys... actually the plain key stuff is pretty boring which is why these glitches are surprising to me.
Ah, you're right... i was thinking of poiting you d7901ec but that's only logging in the transform, what you want is some logging in the
Yeah it's time.sleep... and you'd need to import time of course...
That'd be hard to miss but you only really need one at the top of the main loop:
But see my next message about adding this to output itself, I think that'd be better. |
You'd want to add logging to def send_sync():
_uinput.syn()
time.sleep(0.1) You shouldn't need anything crazy like sleeping for a whole second. |
@rbreaves Have you heard of any similar bug reports? |
This seems super relevant... (though no answer), but it's the same problem you're describing... So perhaps even a 25 ms sleep in sync would help. output.py def send_sync():
_uinput.syn()
time.sleep(25/1000) # sleep 25ms That would kind of suck for typing out long macros though if true... maybe play with a delay before or after sync and if it makes a difference... |
I just ran this 20-30 times in rapid succession doing:
Every once in a blue moon Firefox would lag on the input a hair, but it worked every time, 100% consistently. I definitely think you're experience some type of edge case that not everyone is running into. Note: This using |
Of course I know just enough about how a for loop works that I put a sleep line right at the beginning of the loop. The use of one second was just because smaller values seemed to have no effect. I like to make things blindingly obvious when I'm troubleshooting. def send_sync():
_uinput.syn()
time.sleep(25/1000) # sleep 25ms Alright, so I found this in BTW, had to do another I shortened it down to 5ms ( And, even that very short delay seems to make the macro a lot more reliable for me. I just did the Firefox prefs shortcut like 30 times in a row without a failure. There are the usual variations in how fast it seems to happen, even appearing to enter all the text almost instantaneously at times, while other times it's slow enough to look like someone "typing" really fast. But it worked 100% of the time for as long as I had the patience to test it. If I do it so fast that another tab opens before the first is finished, the full text will now always be there in the URL bar but sometimes it apparently loses keyboard focus at the point where it would have sent the Now to tune the delay. I'm kind of surprised that such a short delay is already effective. The 25ms that the person was talking about in the link about I still would like to understand why none of my attempts to insert a delay in the ... Still working with as little as 1ms as a value. Still seeing an extraordinary amount of variability in terms of how fast the macro actually seems to get sent to the app. Sometimes too fast to see anything, as if the full text was just pasted all at once. Other times, almost as slow as my fastest typing speed on a good day. Very visibly "typing" each individual letter slowly enough for me to watch it. But, it's still succeeding at actually completing the entire macro without errors or missing characters, even after doing about 50 preferences tabs. Even when it acts much more slowly on occasion. I think maybe the variability is simply down to the reliance on the system It looks like my supposition that some kind of input buffer was failing must be correct, in a way. The question is what, and where. I've even had a |
Yeah I understand, just with 1 second you're starting to get into the area where the delay could cause it's own kind of problems is all. :-)
Unfortunately I don't know what to tell you - or where we'd go from here... I don't think adding a 1ms sleep here when no one else is reporting any issues is the correct solution, but I'm also not sure exactly how we'd go about gathering additional data.
That seems strange to me since all that is happening on the Python side is a tight loop and a tiny amount of I/O... I saw some variability but I'd assume it was the Firefox UI, not xkeysnail.
You'd think... but we're talking a buffer with almost 0 capacity? Since it short circuits right away in so many of your tests it would seem the buffer is almost non-existant, which is weird. |
https://stackoverflow.com/questions/58553072/how-to-make-a-delay-at-100-200us Could by Python itself that's the issue as evidentally this isn't what sleep was designed to do... |
https://github.com/torvalds/linux/blob/master/drivers/input/misc/uinput.c Looks like uinput doesn't change that much and the buffer size is 16... 16 events I'm assuming... |
Hey, when you're not catching any fish the usual way, a round stick of material capable of rapidly oxidizing after ignition can come in handy. 🤘
I find it very strange that nobody else has seemingly ever had this issue. I would attribute that to most users simply not using macros much, and an even smaller percentage attempting to use long macros with a higher probability of failing. Unless it really is somehow the Kinto branch that has this issue, but in that case I would expect other Kinto users to complain, especially about the Firefox preferences shortcut. But it's not even limited to Firefox, I had a similar issue with Chrome at times when I tried to use a similar macro to get to settings. And of course the testing in the text editor from the first post. I don't see adding a 1ms delay to be harmful, but that's just my perspective. The question is why does such a ridiculously short delay, apparently the absolute minimum possible with
There is a significant delay when I try to hit the shortcut multiple times, but when the entire buffered output (could be many lines, nothing shows up as long as I keep tapping the shortcut too quickly) finally shows up in the app, there are never any errors. No missing I just held Ctrl+Shift+Alt and "buzzed" the "t" as fast as I could, to do this macro. Nothing shows up for at least 5 seconds, then I get 25 new perfect lines of the same text. No problems other than the delayed output. I can't even pretend to understand exactly what is failing without the delay. The buffered macro lines I'm inserting into the text editor are obviously WAY more than 16 characters, yet they never have errors. So the I need the patched 0.3.0 Uh oh, looks like I was too optimistic. I finally got a couple of lines to show up in the text editor with missing characters. Haven't changed anything since previous testing.
This is doing it slowly, letting each line appear before trying to do it again. Interesting that these failures are near the end, where testing in the first post almost exclusively saw failures near the beginning of the macro. Doing dozens more lines in the same way results in no other errors. I don't know what to tell me either. |
Well so far that's the data... and for absolute empirical reported data we have only our individual experiences... mine is rock solid with no delay added... so that paired with no other reports make me think you're the outlier - though your points about not everyone using long macros is definitely worthy of note.
You lost me here... I thought you were saying it was working so well only AFTER you had applied the delay... I'm not sure what data point you're trying to explain in this paragraph. |
You said So, somehow something is holding onto all those characters in memory and then finally inserting them into the app. A lot more than 16 characters worth. I'm just saying I don't really understand what relationship the small And yes, this is only working with 99% reliability after inserting the delay. Before that it really was about 50/50 as a long term average. Sometimes it seemed to work pretty well, other times it was even worse than usual. Data point: Happens in Ubuntu 22.04 (and previous iterations going back to 20.04 on the same machine). Also happens inside a VM running KDE Neon, where Kinto/xkeysnail is disabled on the Ubuntu host while using GNOME Boxes, and Kinto/xkeysnail is installed inside the VM. |
No, events. A key press is typically 3 events, the scancode, the key event and then a SYN... our artificial output is usually just two events, but that's 4 events if you're counting key up and key down. So a single letter is often 4-6 input events.
But if this was with the 1ms delay added then you aren't really stressing the buffer much at all since you're giving it time to drain... Anyways if processes are waiting on keyboard input then putting anything into the buffer should wake those processes up... allowing them to read the keyboard before the buffer fills - that's how it's supposed to work. |
You'd just have to "suffer" with the normal linux keystrokes when testing 0.4.0.... you can do all the same things, you just have to hit the REAL keys linux wants. But I don't really think that's your issue. |
Data point: 2007 MacBook running recent install of Fedora 36 KDE spin. Core 2 Duo, 6GB mem. Even without the delay added, inserts output in Firefox relatively slowly. More reliable than the other machine but I was able to get it to fail several times out of about a hundred tries. More frequently as the machine struggled to keep up with so many tab being opened. Most commonly it just wouldn’t hit Enter at the end of the macro, but partial insertions of text also occurred. Another: Machine almost identical to the main system, but slightly slower CPU (Ryzen 3500u, 4c, 8t) and half the RAM (16GB). Running Fedora 36 (GNOME 42, like Ubuntu 22.04). No delay added, has a macro error about 5% of around a hundred test tries. Add the 1ms delay, can’t produce a failure in at least two hundred preferences tabs.
I see. So just a couple of characters really. |
Did a After opening probably at least two hundred prefs tabs I haven’t had a single failure. Just the usual momentary delays at times when the machine can’t quite dedicate enough cycles to the xkeysnail process (as I open ridiculous numbers of new tabs each minute without bothering to close any). But no actual failure to complete the macro. So something related to the Kinto patch? |
Thanks for all the input, by the way. At least it led to finding a 99% reliable mitigation solution. A vast improvement for my daily driver system. |
You'd think that if you can jump back and forth between "have problems" and "zero problems" just by using kinto or mainline... but damn it's hard to say where... they honestly aren't THAT different... Ideas:
|
No, adding those lines from the commit and removing the delay line results in nothing good. Back to frequent partial failures of the Firefox macro. As always, I don't really know how to implement the logging commands. I tried Ah, things like |
Did some reformatting of the output. Still trying to figure out which parts are truly irrelevant and can be left out. This feels kind of odd. I'm seeing two release events every time I press and release Right Ctrl, Left Ctrl, or Alt, but only one release event for Shift. Is that normal for mainline
|
Wonder if this has anything to do with anything. I see a ton of press/release events for RIGHT_CTRL during the whole macro, even if I make a point of hitting Ctrl+Comma and releasing the Ctrl key as quickly as possible.
Without the delay there was quickly a failure in Firefox, but I can't see any corresponding failure in this log output. It seems to always show the full macro in the terminal even if it didn't actually work in the application. |
Expected.
Yep, you've found it. And look at shift also... this would seem to be a bug/flaw in the design of how Kinto's handling keystate and how it wants to persist the modifiers until the full combo ends - shift getting "stuck" half on/off is definitely a bug. The buffer just can't handle all that [unnecessary] data being thrown at it so very fast. If you added more modifiers (right shift, alt) I'd imagine you'd see things get progressively worse and worse. This might point out an overall flaw in the overall engine as well... that holding and reasserting the original "real" input state should perhaps be handled differently across large sequences of combos/keys:
|
Even then I wonder if we might find that in SOME legit edge cases one could kill the buffer in NORMAL usage and that we might need to build in a throttle so that if we detect we're pushing too much data we handle flow control ourselves. @RedBearAK Your welcome - and thanks for putting in the time to do the testing, it was great to just toss you ideas and then you come back with test results. I've learned a lot here about the underlying engine and input processing. |
@RedBearAK You might have better luck with a throttle.... so have a global counter, and you count the writes to uinput... when you get close to the buffer size, you add a LONGER delay (now we can afford more time because we're doing it less often)... then reset the counter. This is a stop gap solution though - really the engine needs to just not send all the raise/press keystrokes. |
I was having issues that reminded me of this... the Often it's fast enough, but not always. I wonder if we might always need a small delay between long sequences of keystrokes... This is kind of a problem with feeding input to apps when we have no idea if they are truly ready for that input or not. |
That is definitely an issue, not knowing if you're going to be typing in the right place.
I had that very same trouble with Chrome, and only on one machine if I remember right. Other machines would seem to work perfectly with that same macro. I'll bet it was the same laptop that gave me so much trouble with macros in general. I remember that I changed the Chrome prefs macro to be more like the Firefox prefs macro. In other words I made it open a new tab and type "chrome://settings" instead of trying to use the quicker keyboard shortcut. It seemed to work a lot better for me. Now I realize that was probably just because it took a fraction of a second longer to operate. Most likely with the artificial Between cutting out some of the redundant key press events and adding the 1ms delay in the output loop, this issue could probably be solved in 99.99% of cases. |
I forget how much delay you're using but I first tried a generic 1ms delay after ever command and that didn't fix it, that's when I just made a PR to add the 10ms delay in the config itself. |
Although your delay is per key, so that's entirely different I suppose. |
That's the only one that ever had an actual observable effect for me. And I just had a thought that it could be even more effective if the sleep came first, instead of last. That way there's a tiny delay even before the very first event. def send_sync():
time.sleep(1/1000) # sleep 1ms
_uinput.syn() |
You wouldn't need a delay before the first event... that's the safest one, empty buffer. |
I was thinking more that it would give the receiving window another fraction of a second to be ready to receive input, but of course for the very first event the window wouldn't have received the shortcut yet to even trigger a new tab or something, so yeah. Silly idea. |
I just noticed this (or for some reason it just clicked)... if the events make it to evdev then the problem isn't the uinput, it's higher up... can you test your broken setup again but (while also breaking it in the UI) have If evtest shows the events but they don't make it thru this it's a higher-level issue like libinput or something. |
OK, I removed the This is with the Kinto branch, as always. |
Could you run |
I'm not immediately seeing exactly how to interpret this output. It seems much less chatty about individual key press events. I'll have to spend some time looking at it, but maybe you have better eyes. This is the full output of a run with Actually this seems to be more talking about window/focus events, which might have been relevant to the poster having issues with clicks. Not so much missing keystrokes. Yeah, I did |
Oh sorry xev probably needs the focus when you run the macro, so you might have to tweak your config to allow that... it only shows events that go to it. |
Oh, so if I put a macro shortcut in the terminal block and run it while |
No, I think you need to do it on top of the xev window... so xprop it and make a macro just for it... then trigger the macro there... and we're wanting to see if xev drops the ball... in which case I think we can go to the libinput people. |
Oh, the little window itself. Sure. Makes even more sense. |
Well, this is extremely contrived in order to get something human readable from the extensive output, but:
The Saw the same kind of pattern of incomplete lines whether I held the modifier keys the whole time, or did each shortcut individually.
The spaces were
But... before doing all the filtering for readability I was unable to distinguish any instance of incomplete macros when everything was vertical. The output in the terminal window was strangely inconsistent, but whenever I would quit the Of course, early on in the filtering it's grepping only for the "XmbLookup" lines, which seemed most relevant. There are other "XLookup" lines that are not in this output. Without the filtering the output from even a single instance of the macro is quite extensive and difficult to sort through visually. |
There's the capital "S" in the output above, and then this capital "O":
I find that very peculiar. Neither of those letters should be capitalized, and the "O" is separated from the capital "T" by multiple letters in between, yet somehow it got capitalized also. |
Just to be thorough (25 macros in each run) I put the
|
Ok, so if I'm reading this right |
I'm not even sure how to describe the issue, or how it relates to |
And didn't we already figure out that this problem is basically nonexistent outside of the Kinto branch of |
Did we? Did you? That's not something I can confirm. I can't reproduce it, but that doesn't mean it's not an issue... but you've tried my code and 0.4.0... does the issue magically go away? If so then I'd consider it fixed. If you're still having the issue on 0.4.0 or my codebase then I'd say there is something deeper going on. |
I mean my version (and kinto mainline) sends a lot less bogus events, so that [seems to] HELP, but did it FIX it? I dunno... you tell me. :) |
I don't think there's strong evidence that it's never a problem on the other branches, necessarily, just that it doesn't seem to happen there in an easily replicateable way. I just want to make sure there's real evidence here that it's somehow a |
Well, just because it doesn't happen doesn't mean we're actually fixing the problem... it sounds like something is out of whack - things should not just "get lost" (or corrupted) between the Linux layer and the X layer... so your last tests likely proves there is some problem somewhere - for reals. And libinput would be the first place to check - they might have ideas to go further. IE, it could be kinto is a "best case" for putting the right kind of pressure on the system to reveal the problem - not that it's itself causing it. But hard to say. If you're not really experiencing it anymore I'm happy to let this drop on the floor for now... enough things to focus on. |
Sorry to revive an old issue but, from reading the thread I'm not sure exactly what the workaround is, and I started experiencing this myself. @RedBearAK where did you actually put the |
So, the fundamental problem here is that something (kernel input?) seems to get confused on the timing of the modifier key press-release cycle, which is theoretically being "pressed" before the non-modifier key it is supposed to affect, and "released" after that key is released. This was an order of magnitude worse inside some GNOME Boxes virtual machines I was testing. To the point where even a lot of common shortcut combos were failing to do what was expected. On bare metal the issue is usually fixed with just a millisecond or so of "space" around the "normal" key press. In the VMs it took adding a window of about 50ms before and 70ms after the "normal" key press-release to provide a 99.9% cure for the issue. I haven't used throttle_delays(
key_pre_delay_ms = 2, # default: 0 ms, range: 0 to 150 ms, suggested: 1-50 ms
key_post_delay_ms = 4, # default: 0 ms, range: 0 to 150 ms, suggested: 1-100 ms
) This is just an example from my bare metal install on Fedora, on a mid-range Ryzen laptop. Here's the pull request that added the new API function. The delays are in the https://github.com/joshgoebel/keyszer/pull/134/files The new API is in mainline cd ~/Downloads
git clone https://github.com/joshgoebel/keyszer
cd keyszer
pip install --upgrade . |
Thanks for the detailed steps -- I had been considering switching to |
There's a custom config in the comments on this pull request, along with some instructions for setting up Or, a much newer version of the config that I'm currently using is posted further down in the thread, which does not require messing with the keyboard type modmaps. With the last version I posted in the thread there may be a problem with an extra extension on one of the "include" files, like ".py.py". Have to fix that. Otherwise, it should be a good starting point for a Kinto-like config for |
I've noticed that when trying to use macros they have a tendency to somehow crash or just stop outputting in the middle. Specifically this is with a multi-keystroke type of macro, like:
Ordinarily I would expect this to output:
With one line for each time I trigger the shortcut.
But what often happens, even if I wait a second between each trigger of the shortcut, is things like this:
I thought maybe the more keys in the macro, the more likely one of these hiccups will happen. But it actually often seems to happen right near the beginning of the macro sequence, if it's going to happen at all. Notice how there are a number of lines where the macro stopped after the first, second or third character, but really no lines that stop near the end.
Is there any way to tighten up the code to keep this from happening? Seems like the function responsible just develops a poor working memory at times. Or a buffer is getting overwhelmed with keystrokes coming in too fast? Maybe the keystrokes need a few milliseconds of delay before the next one?
I don't know, I'm just throwing out ideas here.
@mooz @Lenbok @rbreaves
The text was updated successfully, but these errors were encountered: