Is there a way to get the result of which modifier key was pressed that let a "pause until modifiers pressed" action complete? If there is, my search fu has failed me and I apologize.
I'm using something similar to the technique described here: Testing which modifier keys are pressed… A simple way of modifying the actions of your macro by adding the OPT key when you call it to detect which modifier key was pressed, but anyone who has worked with real-time systems knows this technique has a race condition. In my case I have a pause until one of the modifiers is pressed and then a check on which one is currently pressed.
This works well the majority of the time but often fails when the system has a high CPU load, like during backups or running HandBrake. In that case, I press and release the control key but because there is a longer delay in executing the second action than it takes me to release the control key, the macro doesn't see the key still down and executes the else clause thinking it was the shift key.
The workaround is to try and remember to press and hold the control key for a longer period of time, but muscle memory defeats me because the macro works the vast majority of the time.
I suppose I could detect that no modifiers were down and prompt myself to try again and hold the key longer, but that's a kludge.
I really need to be able to query the result of the previous action to know which key enabled the action to get out of the pause. I suspect this is not trivial due to the large number of conditions that the pause action allows. Anyone have any brilliant suggestions?
Hey there, yes there is a token that will tell you the exact trigger, and if it contains modifier keys it will include those.
The example macro below shows you what the trigger (and in this case modifier key(s)) were. You can use this token to determine which modifier keys were used and run the macro accordingly.
Show which modifier key(s) were pressed.kmmacros (1.8 KB)
Macro screenshot (click to expand/collapse)
Thank you for the suggestion, however it only tells me what triggered the macro, not what modifier was pressed exiting a Pause until action.
Doh! I misread your post apparently. Let me rethink things!
If the modifiers are part of the original trigger, then I second @cdthomer's suggestion. If not, I don't have a brilliant suggestion, but maybe an acceptable kludge that will train your muscle memory: insert a sound (the volume can be adjusted) before each of your Set Variable actions. Then train yourself to hold the modifier until you get this friendly feedback.
This is what I'm trying now. I've surrounded the Pause and IF actions with a While. This way, when I press and release the key faster than the CPU allows the IF action to execute, it will wrap around to the While and pause again. What I will see is that nothing seemed to have happened, and I'll press and hold longer, which should work. This is still clumsy though.
The downside to this method is that the whole macro pauses waiting for a keypress. Fortunately for me in my particular macro, that's fine because I'm doing manual stuff. But it will not work for macros that loop executing other actions until one or more modifiers are pressed.
That might work. But maybe Until is a better wrapper.
You are quite correct that the Until is required because it's possible that I have one of the keys down before the While executes the first time and it skips over the whole variable set actions. Thanks for finding another race condition.
In this case you could just have two separate If actions, right? Or am I missing something?
I think two separate IFs still introduces a race condition. Under high CPU load the modifier conditions can change between the two IFs resulting in the logic failing if we really want to detect when only one modifier is pressed. If I press the control key the first IF could detect that only the control key was pressed, but by the time the macro runs the second IF to check if only the shift key is pressed, it's possible I released the control key and pressed the shift key in which case both IFs have been satisfied, which may not be desirable. The two IFs method might work or not work depending on what's desired and how severe the race condition is caused by high CPU load.
This is why I think it's important to have the result of the pause action recorded so we can test it safely at any time.
This is the corrected logic using the Until. This should ensure that the macro will not proceed until either the control or shift keys are pressed, but not both at the same time. Under high CPU load it may require a longer keypress than normal to ensure the correct key was detected, but that's fine in my case.
You are a fast typer. Clearly I don't fully understand your requirements.
But maybe you now have an approach that works. If not, I suggest that you post your macro.
It's not so much that I'm a fast typer, pressing and releasing the control key, as it is with the CPU load that slows down KM to the point that the race condition is opened wide enough to be a problem. Note that the race condition is always there waiting to bite; it's just not hit under "normal" CPU loading conditions.
The issue occurs when my backup software is doing budget enforcement. It really loads the CPU and file system. Combine that with HandBrake running at the same time and the CPU is maxed out when I try to run the macro. I determined it was a race condition when I found out that the macro runs much more reliably when the CPU is loafing along. I stop HandBrake when I need to run the macro, but I can't stop the backup software without it causing problems.
What my macro does isn't important. The issue is in trying to reliably detect which one and only one modifier key was pressed. I'm trying to find a solution that will work with any macro so I can reuse my logic.
However, thinking more about the Until logic, it still has a race condition. It's extremely unlikely, but that's what makes race conditions so insidious. They're infrequent and drive you crazy when they happen.
The current flaw is that I can press and release the control key before the IF runs causing the IF to set the variable to the wrong value. The Until was supposed to check that the key I pressed to exit the Pause was still down but … what if before the Until runs I then press the Shift key, perhaps by mistake. The Until is satisfied and exits leaving the variable set to the shift (wrong) value. Sigh.
As I said, this is extremely unlikely to hit two race conditions so quickly, and I'm not going to worry about it in my macro because no harm is done if that second race condition is hit too. I can only do so much without an atomic action setting the result of why the Pause exited.
At this time I see no way to reliably determine 100% of the time under any CPU loading conditions what one and only one modifier was pressed to exit the pause. It would be great if that could be corrected by having the Pause action atomically update a variable with the correct value.
Sounds like waiting for the friendly feedback might be the best way to go. If you don't like the idea of a sound, then you could use a brief notification. I suggested a sound because the feedback is faster. Using both is likely the best way to go.
The goal is not to have either a sound, a notification, or simply nothing happen until the key is pressed longer where how long is dependent on CPU loading. As long as pausing and checking which modifier was pressed are two separate actions, there will always be a race condition where the modifiers can change. I honestly don't see a reliable workaround without support by KM atomically updating a variable when the pause exits.