Multiple macros with Clipboard Changed trigger: Order of execution?

If you have more than one macro that uses the Clipboard Changed trigger, how does KM determine the order in which they will run?


I have a macro (that uses this trigger) that's increasing in complexity, so it would be good to split up. But I'm wondering the best way to do that.

  • Split it into multiple, each with the Clipboard Changed trigger. (Hence my question above)
  • Split it into multiple, but assign no trigger to each and create a parent macro, triggered via Clipboard Changed, and use two or more Execute a Macro actions, so I can control the order.
  • Go even more complex and convert the child macros to subroutines, take in variables, return variables, etc. (I do not want to do this :wink: )

This question could be generalized to what the order of execution is for the type of triggers that don't spawn the Conflict Palette.


Cross referencing:

I have no idea about the order of operations, but…

…that's probably exactly what I'd do, just to keep the number of macros getting triggered by a clipboard change as small as possible. Fewer triggers means less chance for some sort of strange conflict.

-rob.

2 Likes

Yeah, this seems like the cleanest/simplest thing to do.

But I think it would be good to have some documentation about these cases.

I couldn't find anything about this.

(Although I don't know what could be done about it, other than applying this type of macro structure in those cases.)

What if the documentation said, "You cannot rely on order of execution for any trigger in KM," then would you be asking for a change in the language to fix it? Or are you satisfied if the documentation says that the order is not selectable?

I think just noting this reality would be fine.

It would be the documentation equivalent of the Conflict Palette.

The functionality of the Conflict Palette needed to be created to handle certain types of triggers. So when I stumbled onto the question of triggers whose nature doesn't require the Conflict Palette, I thought some type of explain of "what will happen" would be good in leu of an actual function.

Like I mentioned in my last post, I don't know what kind of built-in mechanism (to control the order) would be good. So no "feature request" for me (yet) :slight_smile:

But given that there seem to be a number of triggers that fall into this category, it might be good to document this concept and how best to handle it. Even if that's just a post here in the forums.

It might already be possible using semaphores to control the order of execution. Have you considered using that action to control your order of execution?

For example, carefully consider these two macro:

...and...

Both of these macros will trigger on the "clipboard change trigger". Both of them will be able to lock the first semaphore because of the 0.2 second pause action. (Surely the KM Engine won't take longer than 0.2 seconds to start both macros.) Then both macros will reach the second semaphore lock actions and that will lock both macros, because the first and second semaphores are swapped in each macro. Think about it.

From there, it's easy to make one run "first", which is simply by having a shorter timeout on the second semaphore lock. For example, if macro T1 had a 0.1 second timeout on its final action, while macro T2 had a 0.2 second timeout on its final action, then you have made the T1 macro "execute first."

I haven't actually tested this idea, but it seems to me that the idea should work. Do you want to test it? If it works, I want a couple of handclap emojis.

2 Likes

I haven't had good luck with my testing/usage of semaphores. But they are a useful tool.

Very clever and good thinking.

In principle this sounds like it would work. But my practical concerns would be:

  • Having to coordinate the timing across multiple macros.

    I suppose you could set a global %SemaphoreDelay% variable and then do a calculation (-0.1) in the macro that should execute first. But still, multi-macro coordination.

  • Placing the "order logic" in each of the macros (which could be in different folders) instead of a single "parent" macro that has your single use of the Clipboard Change trigger. That seems like you're asking for trouble. I know there are some ways to try to mitigate that but I bet those would just add to the complexity.

You get those even without the test: :clap:t2::clap:t2::clap:t2:

Thanks for the compliments, buddy. I appreciate it, since not everyone is a fan of my ideas.

The solution I provided was limited to prioritizing only two macros. I suspect it could be expanded to support 3 or more, and that might make my solution more complicated. But for just two macros that require coordination, this is a very simple piece of code, and I hope someone finds it useful in the future.

1 Like

Speaking of documentation/terminology:

Are there names for the functional categories of triggers?

Casually looking through the triggers, I see two categories that jump out:

  • State (Clipboard changed, Sleep, Wake, etc.)
  • User initiated (Hot key, Macro palette, Menu, Gesture, Dragged, etc.)

But I'm not sure if that is the line between them.

Also there are some that are tricky, like "Folder trigger", which could be initiated by a user or the system. So "User initiated" wouldn't necessarily be correct.


This categorization probably doesn't matter though. So trying to find the perfect delineation and words for each is probably a waste of time.

I was just* curious.

(*But maybe they would be useful? ex: Grouping the triggers in the Triggers Menu.)

I wouldn't object if the editor is changed to group them by category instead of alphabetical. But I would prefer keeping them alphabetical while adding a little category icon beside or after the name. Perhaps like this:

image

However after looking at the mess, I wouldn't see much benefit to it.

I'm not 100% sure, but a quick test suggests that they run in the order the Engine comes across them when it parses the macros.plist file. Which makes some sense -- "here's a clipboard changes trigger, add that to the end of the when the clipboard changes trigger list...".

So the rule of thumb would be that the order of execution is

  1. Primary sort by Macro Group name
  2. Subsorted by creation date

But I wouldn't rely on such undocumented behaviour. Assuming you can't limit which gets triggered by context -- active app, document name, etc -- I think I'd go down @griffman's route and aim for a single trigger macro that conditionally executes other macros. That'll keep the logic in one place, uncluttered by the actual actions to be performed, making for easier maintenance.

2 Likes

Ha! Thanks for the taking the time to mock your idea up.

I agree that it provides some good additional context (clever use of the mouse emoji for USB triggers) but, as you wrote, I don't know how useful it is.

I can maybe imagine it being divided into only those 2x big-picture categories (like I mention above). But maybe not too. And any more than that would probably be confusing.

Yes, that makes sense. But without a UI, it's both hard to know and do anything about.

Of course. Hence the request to have it documented :slight_smile:

That would be a great feature to use in some sort of KM obfuscation contest. P.S. Your diagnostic skills are second to none.

Documenting it only really makes sense if the behaviour is a) reliable and b) useful.

While the rule of thumb above may well be correct, the order of execution could change every time you add a new clipboard change trigger, move a macro with that trigger between Groups -- possibly even when you rename a Group...

IMO, if it was documented it would have to hedged with so many caveats as to be meaningless in real world use -- and the same goes for any event trigger used across multiple macros that doesn't spawn a Conflict Palette.

Far better to make the behaviour explicit yourself than to rely on an assumed "execution order" -- especially when said order is only "when macros start executing", not when any particular actions after the first of each macro may occur!

By "documenting" I just mean shining some light on it.

Adding something to the official documentation could be good. But even what we're doing here is helpful and what I was looking for when I first stumbled into these questions.

Or making it explicit that it's unreliable and not useful, therefore don't rely on it and don't it :wink: [1].

The Wiki is full of helpful information and notes like this:

The Wiki also has links to relevant forum posts (maybe like this one).

I think there's value in a statement like this (somewhere) for future readers:

Many factors outside of the user's control determine the execution order of multiple macros who are assigned this trigger. Where the Conflict Palette exists to handle cases like these for the Hot Key trigger, there is no such user interface for macros assigned this trigger.

If you need to control the execution order of multiple macros that rely on this trigger, consider:

  • Creating a single parent macro and assign it this trigger;
  • Creating child macros* (without this trigger assigned) that contain your goal's actions and logic;
  • In the parent macro, use multiple instances of the Execute a macro action to reference those child macros and order them as needed.

*Subroutines would also work.


  1. Especially given the precedent set by the Conflict Palette. Which was created specifically to allow for multiple macros with the same trigger. This is the opposite of that. Therefore I think it would be good to point out that deviation. ↩︎

Perhaps this is the misconception? There will be an order in which these macros are triggered, but they execute concurrently -- unless you do something to prevent that. So there's little difference between these situations and any other that involves multiple macros (or instances of the same macro) potentially accessing the same resources at the same time.

Indeed, it's the Hot Key trigger that's the odd one out in that no macro is triggered until all conflicts are resolved to a single (enabled and active) macro.

Caveat author applies wherever there's the possibility of contention, all across KM, so might perhaps be considered "assumed knowledge" (something that pervades any documentation). But that does mean the user must realise that shared triggers are another form of contention -- something which may not be immediately obvious. So a call-out at the top of the "Triggers" Wiki page may well help.

This question does not make actual sense.

Since all macros will start, and macros, while running, all run independently, the next action to start could be in any or the macros. So even if Macro A started first, the first action of Macro B might be the next action to start, and while it was executing the first and second action of Macro A might execute.

To control this, you would need a Semaphore Lock action, but even then there is no way to know which one would execute first - only that once one of them locked the other would not progress until the lock was released.

If you want to control the order, you will need to have a single triggered macro that executes the others in sequence.

This would be a correct solution.

What would you pass in? There isn't any information from the trigger, so shy would it need anything further?

Wow. My premise was all wrong.

I'm sorry I didn't realize this. Is this logic documented anywhere? [1]

I guess I was thrown by the ability to set some actions as async (synchronous by default). I inferred that macros were also executed synchronously. Which would mean (incorrectly) that Macro B would be held up by Macro A if they were both triggered at the same time (by the same trigger).

I didn't realize this until your reply. Thanks for pointing this out.

:+1:t2:

So that means using multiple Execute a macro actions is the way to force synchronous macro-to-macro execution.

(I'm sorry if I'm being dense.)

Yes, I think I see what you mean. In the case of the %SystemClipboard% token, there wouldn't be anything to pass on.

But maybe, in the parent macro, one would want to first run a filter on the value of %SystemClipboard%'s and then pass the result to a subroutine (via a variable like: filtered_SystemClipboard).

Thank you.


  1. I do see this now:

    When you trigger a macro, the Keyboard Maestro Engine takes a copy of it and starts executing it. If you trigger another macro before the first one finishes, that will start executing as well - both will be executing more or less simultaneously.

    [...]

    Although it is rare to have multiple macros triggered at the same time, the normal behaviour in Keyboard Maestro is that all triggered macros run simultaneously, with actions from each independently triggered macro running at the same time.

    But I wouldn't have thought to look on the Semaphore Actions wiki page for this fundamental concept. ↩︎

In those contexts, it is about synchronous within the macro (ie, that action finishes before the next action starts) or asynchronous to the macro (ie, that action sends its work off to be done, and the macro continues on with the next and further actions).

Macros always run asynchronously to each other (with the Semaphore Lock action providing a way to limit that).

In this case, yes. Presuming the Execute a Macro action did not have the Asynchronous option turned on which would then defeat your attempts to run each macro to completion in turn before starting the next macro.

Sure, but in that case you could not have multiple macros triggered by the Clipboard Changed trigger and also do this filtering. So you would have to have a single triggered macro in that case.

Thank you for this.

I was trying to find a way to describe exactly this. I was going to mention something about "thinking at the action level rather than the macro level". But I couldn’t get the wording right and I thought maybe it would be redundant (given your earlier reply) even if I did.

I forgot it had this options. Thanks.


So to close this out:

@peternlewis Do you think it would be a good idea to explain this fundamental KM concept, in the Docs/Wiki, somewhere in addition to the Semaphore Lock page?

Or is there and here enough?

Thanks!