Initiator + Multiple Keystroke Trigger

Hello,

I would like to know if it is possible to have a macro fired by the following kind of trigger. In case it is not possible, I think it would be a good suggestion.

  1. A sequence activator is pressed and released (to keep listening to future keystrokes). This should be a very strange key or key combination, lets say: (Shift + F24)

  2. Once the activator is released, KM should keep listening for a maximum amount of time (to prevent locking the keyboard forever), lets say 3 seconds. During this time, if any known keystroke sequence is found associated with a macro, it is fired and keyboard events are no longer swallowed. If time has been spent and there is no known sequence, keyboard is unlocked to talk with other apps.

Note: keystrokes after the initiator should not be transferred to the focused APP, I don't like the way "this string is typed" trigger behaves, which can be dangerous. In case this is not possible due to OS limitations, the sequence keys could be wrapped by modifiers, such as (alt + Shift + Cmd) + key to prevent any kind of behaviour in the focused APP.

So, for example, if you have the following sequences:

(Shift + F24), i, n, d => indent text
(Shift + F24), c, a, p, s => capitalize text
(Shift + F24), p, d, f => export to pdf

Now imagine you type:

(Shift + F24) , i, x, d => nothing happens, wait 3sec and keyboard is unlocked
(Shift + F24), c, a, p, s => capitalize text macro is fired and keyboard is unlocked
(Shift+ F24), p, d, g, p, d, f => export to pdf (pdg was a mistake)

Keystroke sequence should be any kind of key or key combination, so it should also be possible to to have a sequence like this:

(Shift + F24), (alt +a), (alt + b) => another macro

Does it have sense? It is possible to do something like this?

Thank you!

1 Like

I'd use a feature like that if it was built in.

But if I was doing this in a macro, then I'd look for a way to temporarily redirect keyboard input to somewhere and then the macro could check that. Didn't find anything promising on a cursory search but I might look again later

1 Like

Shift+F24 could trigger a macro that enables a separate macro group, pauses and then disables it. Each of the triggered macros in that group could end by disabling the group.

Enable/Disable Screenshot

Macro Screenshot

Noisneil, thank you for your response and screenshots (I find them very useful). However, in your second screenshot (zz), which is the trigger? I don't like the "this string is typed" trigger since it actually forwards key events to the focused APP, I could work around if KM allowed complex keystrokes such as (option + cmd + shift + z) (option + cmd + shift + z), but as far as I know it is not possible.

What is your recommendation? I don't want "zz" to be passed to the focused app, nor "zz" and then "backspace backspace". This is dangerous

I didn't set triggers for either of the macros, as it doesn't matter what the triggers are; that's down to your preference.

For the first macro, try setting the trigger to a hotkey you'd like to press when you want KM to start 'listening' for macro hotkeys.

The second macro is just a basic example of a macro that disables its own group at the end of its run. You don't need to copy it.

"zz" is just the title of the macro. Ignore it. It has nothing to do with the trigger. I title all my new test macros "zz" so that they show up at the bottom of the group.

Just to clarify, do this:

  • Create a new macro group and name it "Test" or something similar.
  • Create the Enable/Disable macro (above) and set it to control the enabled/disabled state of your "Test" macro group. Put the Enable/Disable macro in a global group. Set its hotkey to whatever you like.
  • Copy a few of your favourite macros into the new "Test" group so you can try them out. At the end of each of these macros, add the action that disables the "Test" group. These macros can be triggered any way you like.

You will now have a group of macros that are only available to be triggered for the period of time specified by the pause action in the Enable/Disable macro. Immediately after one of these macros has run, further hotkey presses will be ignored.

Only when used inappropriately...

Typed String triggers are meant to be used only in text-fields for text abbreviation expansion macros.

It would be nice if Keyboard Maestro supported key chords like this, but unfortunately Peter has decided against it.

There are ways to fake it, but in general for tasks like these you're probably better off constructing either a Conflict Palette or using a Prompt With List action.

Ok, it is a pity, I think this kind of macros could be the most useful ones. I hope it can be considered as a suggestion for future improvements.

Thank you for your responses!

Did you try my "listen for hotkeys" suggestion?

hello noisneil, I think that like @ccstone said KM has no trigger like the one I'm looking for. Thank you for your help

Sure but with my suggestion all you have to do is hit i, c or p for indent, caps or pdf, respectively. It's the same functionality without needing to type the whole string. Perhaps you should give it a shot.

Hi Alvaro

Have you had a look at BetterTouchTool (BTT)? With it you can do the most "absurd" key sequences without transferring anything dangerous to the front app.

Suppose you want to open a new tab in Chrome (normally: Command + T), with BTT you can set up Control + Option + Command for it, pressed in a row. Or whatever you want.

You can set the time in which these modifiers must be pressed and also how much time must pass (since the last keystroke) before you activate these modifiers. And much more...

The mentioned example would look like this in BTT.

Maybe this will help.

2 Likes

Thank you @Frankb !! I didn't know about BTT, I will take a look at it. It looks promising!

Thank you too @noisneil yes, I get what you are saying and it is not too bad, the problem comes when you have programs where tons of shortcuts are needed, like photoshop. Sometimes having multiple keystrokes can help to memorize, since maybe you have something like "merge all" "merge visible" "merge selected" "merge down" and so on... so it is easier to have a key sequence trigger like: ma = merge all; mv = merge visible, ms = merge selected, md = merge down... instead of picking random keys.

You are welcome! Speaking of "tons of shortcuts." I know the problem :slight_smile:

Apart from Key Sequences, BTT can distinguish between left and right modifiers. So "Commad + a" triggers different macros/actions depending on which command key you use.

This gives me more options. But more important: I can remember the shorcuts better if I only use "right command + x" and "right option + x" in certain apps.

1 Like

BTT might be the right tool for you, but here's a KM solution too:

With the following macros, you set the shortcut for all your merge macros to (for example) ⌘⇧M. The next key you press triggers a specific merge type.

For example, if you want to merge visible, you hit ⌘⇧M and then v.


MacroCat - Merge.kmmacros (51 KB)

Macro screenshot

MacroCat - Watch for Keypress.kmmacros (43 KB)

Macro screenshot

"MacroCat" is short for Macro Categories.


Setup:

Add the actions you want to perform into the Switch/Case group.


In Use:

Trigger MacroCat - Merge with ⌘⇧M and then hit one of the following keys to perform a merge action:

v (merge visible)
a (merge all)
s (merge selected)
d (merge down)

For actions other than Merge, duplicate this macro, rename it, set a suitable hotkey and edit the Switch/Case group. The typed key can be any letter or number. If the keypress is found in the Switch/Case group, it will perform the appropriate actions; if you type a key that is not set to perform an action, nothing will happen.


MacroCat - Watch for Keypress simply captures the keypress to be used by the main macro. If you duplicate the main macro for other uses, each duplicate can reference this macro; there's no need to duplicate it too.

I've been working on this as well because I've wanted to use something like this for a while.

I noticed a few other requirements from the OP that appeal to me:

  1. Don't allow keystrokes to press through to the current application
    • These could interfere with the current app
  2. Allow sequences larger than 2
    • While it's true they only need '⌘⇧M > Key' for the macros mentioned – at some point there will be conflicts and a need to expand
  3. Input timeout after 3 seconds
    • Allows the user to exit the input sequence
  4. Ability to use modifier keys
    • More customization

Here is my solution: Initiator Keystroke Trigger.kmmacros (113.9 KB)

Pros
• You get to call commands via character sequences without disturbing the current app
• Lots of control and granularity

Cons
• Adding a new sequence is not quick
• It's easy to make a mistake when adding

Usage

  1. Trigger with whatever you choose (currently Ctrl-Option-Cmd+I)
  2. After the audible tone, type one of the demo input sequences: "ind", "pdf", "caps", or "⌥a⌥b".
  3. Terminate your sequence with 'Return'.

It's currently configured to launch 'Display Text' actions that say what would have been launched. To customize your sequences, take a look at the walkthrough to see what's going on in the macros.

Basically, you can use what's already there to build your own sequences.

Walkthrough

I gotta say that this one is very bulky and requires some manual labor up front when adding new commands – but it does work almost exactly as desired and is pretty nice once up and running.

The main concept here is to assign a macro for every "level". For example, the "caps" command has 4 levels, "pdf" and "ind" have 3 and "⌥a⌥b" has 2. This walkthrough will show how each is encoded in the macro so you can make your own.

Level 1 Setup

The first major thing the macro does is save the current focused application. This is necessary so we can switch somewhere else while a key sequence is inputted – satisfying requirement #1 above:

Keyboard Maestro Export

Then we make the aforementioned switch to the Keyboard Maestro Engine app, which doesn't seem to care that we are focusing it or pressing buttons while focused:

Keyboard Maestro Export

From there you'll hear a sound signaling that you can enter a sequence now. I added this sound because at first I was entering commands too fast and needed some indicator of when to start pressing keys. It's being played asynchronously so we don't have to wait for the sound to end before we can start typing.

Listening for our first keys

After setup, the macro listens for expected keys pressed within a 3 second timeout – requirement #3:

Keyboard Maestro Export

If you click the gear on the 'Pause Until' action above, you'll notice the timeout is set to 3 seconds and both 'Timeout Aborts Macro' and 'Notify on Timeout' are disabled (since the timeout here is a feature and not a bug):

The next section consists of actions specific to each key at level 1.

Here is the start of the 'caps' chain:

Keyboard Maestro Export

The 'InitiatorString' variable is used to keep track of state. When the user eventually finishes entering a command, 'InitiatorString' will be checked.

Then the macro throws execution to the next level, the 'Initiator Trigger L2' macro.

Before we go there, note that there are other keys at this level to understand.

The one that sticks out is the '⌥a' in the '⌥a⌥b' sequence:

Keyboard Maestro Export

Here we want to be able to know if the key and modifier are pressed (requirement #4). This is why I had to use an 'If' action instead of a Switch. The Switch can't check for modifiers unfortunately.

The last key to look for at this level is the 'Escape' key:

Keyboard Maestro Export

No official actions here, but pressing 'Escape' allows the user to abort input without having to wait for a 3 second timeout.

Finally, at the end of the level 1 macro, and all of them, execution runs into this:

Keyboard Maestro Export

'ReturnToInitialApplication' is set to YES by default at the start of every sequence at level 1 (Initiator Trigger L1). Note that if the macro you're calling opens another app and continues there, then you'll want to be sure to set 'ReturnToInitialApplication' to NO right before executing that macro. More on this in the Level 4 section below.

The Next Level

Level 2 is more of the same – just continue adding the next link in whatever sequence you're building.

One difference worth noting is what must appear at every level starting at level 2: a special 'Pause Until' action.

This had to be added after first testing this macro because I noticed that sometimes keypresses were bleeding into the next level. I'd press a 'D' in level X and it would still be observed in level X+1 somehow. To stop it, this 'Pause Until' action will wait for there to be no keypresses.

Unfortunately, this meant hardcoding every key that'll be supported:

From...

all the way to...

Specifically, supported keys are: a-z, 0-9, F1-F19, 'Return', & 'Escape'.

Of course you can add more. I will add symbols and Numpad support at some point, but I got tired of entering each one manually. Just add them at the end of the list. The good news is that you only have to specify them once and then you can copy/paste this 'Pause Until' action at the start of every macro at Level 2 or above.

Notice that you can't add modifier keys here in the 'Pause Until' action. Fortunately, when ⌥ is pressed with a key, let's say 'X', the 'X' registers as being pressed down, so this 'Pause Until' will still work even if modifiers are also being pressed at the moment.

If you want to check for a modifier key, you have to handle this below the 'Pause Until', just as was done in the above example where I showed the start of the ⌥a⌥b sequence.

Level 2 then continues on in the same way that Level 1 does recording the keys pressed and calling the next level.

The other Level 2 difference is that now we are appending onto the 'InitiatorString' variable instead of setting it:

Keyboard Maestro Export

Level 3

This is the first level that includes a sequence termination and macro call. Ideally, we'd want sequences with 3 characters (like "ind" or "pdf") to terminate on level 3 – but we actually end up needing an extra character to terminate everything. That's why here at level 3 we're terminating a sequence with 2 characters: "⌥a⌥b".

The reason sequences are 1 keypress longer than their character count is because the macro needs to know when we're done sending input. A 'Return' key is then sent to terminate the whole thing.

Otherwise, all sequences would have to be the same length. You couldn't have a sequence of length 2 and a sequence of length 4 without signaling when to stop taking input. They'd all have to stop in the same level, and we want the flexibility of being able to create sequences at variable lengths (requirement #2).

Here's how the "⌥a⌥b" sequence terminates in Level 3:

Keyboard Maestro Export

We respond to the 'Return' keypress and check to see if the 'InitiatorString' matches the known terminating sequence at this level. If it does, we activate the initial application so that the macro will return from the focus on the KM Engine BEFORE executing. This is required if you want the macro to execute in the app you're currently in.

Level 4

This section has 2 sequences that terminate – "ind" and "pdf":

Keyboard Maestro Export

Notice how "ind" uses 'Activate Initial Application' and "pdf" doesn't. Basically I made the assumption that a macro named 'Export to PDF' would open the Preview app and go there. It's an example of how to make sure the macro takes you to another application instead of the default – which is to return to it.

This is done by setting 'ReturnToInitialApplication' to NO right before calling the macro intended to run at the end of a sequence.

On the other hand, "ind" does return to the initial application first before executing the macro. This is so execution can continue in the intended app.

Level 5

Finally, the last sequence is terminated in 'Initiator Trigger L5' ("caps"). Nothing special here other than it being the longest sequence. This macros will handle sequences of any length – but you'll have to add another level in the macro chain every time you add a command with a larger sequence. It currently supports sequences with 1-4 characters, which will get you a long way, but it helps to know you can get more.

..and that's it!

Go ahead and edit the macros to create whatever sequences you want!

New Sequence Quick Guide

  1. Each new key needs to be added 2-3 times per each level:
    a. In the collection of 'Pause Until key is down' specific to that level
    b. In its own 'If key is down'
    c. [L2+ only] In the first action entitled 'Pause Until None of the supported keys are down...'. Note that a-z, 0-9, F1-F19, 'Return', & 'Escape' are already added.

  2. 'If key is down' contents (before terminating)
    a. 'InitiatorString' must be set in L1 with the first letter and then be appended to in L2+
    b. Call the next level macro, Initiator Trigger L(X+1)

  3. 'If key is down' contents (while terminating)
    a. A sequence ends in (character sequence count +1) with the 'Return' key
    b. Call any macro from there based on the contents of 'InitiatorString'
    c. If you want to return to the app you were in before inputting a sequence, then call the 'Activate Initial Application' macro before calling your sequence ending macro
    d. If the macro you call switches applications, then set 'ReturnToInitialApplication' to 'NO' before calling that particular macro

Wish List / Future

• Some sort of visual component. I'd like to at least see what's been typed so far and if possible see what commands are still available in real-time while typing. Some kind of updating HUD.
• An audio beep after entering an unsupported sequence

1 Like

Wow, thanks for your kindness! :man_bowing: I have to try that, you made a good work there :clap::clap:

That's a lot of work right there.

Personally, I think this is overkill and, in removing one problem, presents new ones. My previous suggestion is more than adequate for most needs, I think. However, may I humbly submit two alternative approaches to using strings as a trigger? This is primarily with a view to simplifying the setup.


1

Pros: No lengthy setup procedure or need to wait before entering the trigger string. Any letter or number combination, of any length, can be used.
Cons: No modifiers allowed. Requires an extra piece of software.

I have opted for the actions to run upon hitting Return, but if preferred this can easily be set as a timeout instead.

You will need this small, free app, KeyboardLocker, which temporarily disables keyboard input, preventing the typed string keystrokes from interfering with the app you're working in.

In Use:

As in my previous suggestion, trigger using ⇧⌘M, and then type the desired string followed by the Enter Key. If the typed string is found in the Switch/Case group, the related actions will be performed; if not, nothing will happen.

MacroCat String - Merge.kmmacros (27 KB)

Macro screenshot

MacroCat String - Watch for Keypress.kmmacros (36 KB)

Macro screenshot

NB: to manually exit KeyboardLocker, simply hit ⌘Q.


2

...and here's an even simpler approach using a prompt. This does require the use of the Enter Key, so may or may not be desirable, depending on personal preference.

MacroCat Prompt - Merge.kmmacros (42 KB)

Macro screenshot

2 Likes

Definitely more concise. Most users probably won't need the modifier keys anyways.

The KeyboardLocker is a good find, although I was a tad distracted with the 'Keyboard Locker Activated' message. Still usable enough though.

I also don't understand how KM can still listen for keys if Keyboard Locker is active - but hey, it works :+1:

1 Like

I'm guessing it might be because the keystrokes aren't actually coming from the keyboard...?