Applescript - ignore modifier keys I have an applescript that needs to be called by an external program with command-shift-click.  However, the applescript then performs keystrokes with command &amp; shift held down.  How do I get around this?  

I tried:

keystroke "blah" using command up


but get this syntax error:
Expected end of line, etc. but found application constant or consideration.

I've similarly tried

key up shift
key up command
keystroke "blah"


and this simply doesn't work - the command/shift keys are still held.

Is using documented anywhere that might help me resolve this?  Otherwise, how do you make the applescript ignore user-held modifier keys?

EDIT: A workaround (but not a solution): https://stackoverflow.com/questions/6584357/why-applescript-always-send-keystrokes-with-command-down  add delay 0.2 before the script
The most robust solution is to make a system call that checks which modifier keys are currently pressed.  The following solution uses small external executable written in C that you can compile yourself from the command line (provided you have Xcode installed).

If you don't know C, don't worry!  You'll only need to copy and paste the C code.  It's called using a simple AppleScript handler.

Technical Note: The following code is based on macOS's Carbon framework.  It should also be possible to create a Cocoa solution that would allow everything to be called directly from AppleScript using AppleScript-Objective-C.

Create the Compiled Executable

Follow these instructions to create a command-line program that prints the current modifier keys. You must have the Xcode Command Line Tools (or the full Xcode) installed in order to compile the code.


Save the following code as getModifierKeys.c in the Desktop folder:

#include &lt;Carbon/Carbon.h&gt;

// Define the key code for the fn Key.
const unsigned int fnKey = 131072;

// Define the accepted labels for the modifier keys. The first label is used by the program for output.
const char *cmdLabels[] = {"command", "cmd", "@", "⌘"};
const char *controlLabels[] = {"control", "ctrl", "ctl", "^", "⌃"};
const char *optionLabels[] = {"option", "opt", "alt", "~", "⌥"};
const char *shiftLabels[] = {"shift", "$", "⇧"};
const char *fnLabels[] = {"fn", "function", "func"};
const char *alphaLabels[] = {"caps", "caps lock", "caps_lock", "caps-lock", "capslock", "alpha", "⇪"};

// Define the order to use when returning the modifier keys.
const unsigned int modifierKeyValues[] = {cmdKey, controlKey, optionKey, shiftKey, fnKey, alphaLock};
const char **modifierKeyLabels[] = {cmdLabels, controlLabels, optionLabels, shiftLabels, fnLabels, alphaLabels};
const unsigned int modifierKeyLabelsCount[] = {(sizeof cmdLabels / sizeof *cmdLabels), (sizeof controlLabels / sizeof *controlLabels), (sizeof optionLabels / sizeof *optionLabels), (sizeof shiftLabels / sizeof *shiftLabels), (sizeof fnLabels / sizeof *fnLabels), (sizeof alphaLabels / sizeof *alphaLabels)};

// Get the length of the above arrays.
const unsigned int modifierKeyCount = (sizeof modifierKeyValues / sizeof *modifierKeyValues);

// Define the label to use for no modifier keys.
const char *noneLabel = "none";

// Declare the helper function to determine matches to the labels.
int arg_match(const char *, const char *[], int);


// Main function.
int main (int argc, const char *argv[]) {
    // Get the current modifier key codes.
    unsigned int current_modifier_keys = GetCurrentKeyModifiers();
    unsigned int i;
    unsigned int modifiers_count = 0;

    for (i = 0; i &lt; modifierKeyCount; i++) { // Loop through all possible modifier keys again, and print the label.
        if (current_modifier_keys &amp; modifierKeyValues[i]) {
            if (modifiers_count) { printf(" "); }
            modifiers_count++;
            printf("%s", modifierKeyLabels[i][0]);
        }
    }

    if (modifiers_count) {
        printf("\n");
    } else {
        printf("%s\n", noneLabel);
    }

    return 0;
}

// Helper function to determine matches to the labels.
int arg_match(const char *arg_string, const char *key_labels[], int key_labels_length) {
    for (int i = 0; i &lt; key_labels_length; i++) {
        if (0 == strcasecmp(arg_string, key_labels[i])) {
            return 1;
        }
    }
    return 0;
}

Compile the program by running the following commands from Terminal:

cd ~/Desktop
gcc -framework Carbon getModifierKeys.c -o getModifierKeys

You will now have an executable file named getModifierKeys on your Desktop. Move this to wherever you want to store it.


Call the Executable from AppleScript

The following handler is used to call the executable for AppleScript. The handler will block until the specified modifier keys are no longer pressed.


Copy the following handler into your AppleScript, and replace /PATH/TO/EXECUTABLE with the absolute path to the executable you created in the previous step (if you didn't move it, it will be located at /Users/USERNAME/Desktop/getModifierKeys).

to waitForModifierKeyRelease(modifier_keys)
    (*    (string OR list of strings) → nothing

    Block execution until all of the keys specified in modifier_keys are released.
    Modifier keys are specified by their name; multiple keys may be specified in a space-delimited string or in a list.
    The possible modifier keys are "command", "control", "option", "shift", "fn", and "caps".

    Parameters:
    modifier_keys [string OR list of strings] : The modifier keys to await release.

    Result:
    [nothing] : No return value.    *)

    set should_wait to true
    repeat while should_wait
        set current_modifier_keys to do shell script quoted form of "/PATH/TO/EXECUTABLE"

        set should_wait to false
        set should_wait to should_wait or (current_modifier_keys contains "command" and modifier_keys contains "command")
        set should_wait to should_wait or (current_modifier_keys contains "control" and modifier_keys contains "control")
        set should_wait to should_wait or (current_modifier_keys contains "option" and modifier_keys contains "option")
        set should_wait to should_wait or (current_modifier_keys contains "shift" and modifier_keys contains "shift")
        set should_wait to should_wait or (current_modifier_keys contains "fn" and modifier_keys contains "fn")
        set should_wait to should_wait or (current_modifier_keys contains "caps" and modifier_keys contains "caps")
    end repeat

    return

end waitForModifierKeyRelease

Call the handler before the keystroke command (or any other commands that behave unpredictably when modifier keys are pressed), specifying the keys that you don't want to be pressed.

my waitForModifierKeyRelease({"command", "control", "option", "shift"})



The "fn" and "caps" modifiers are not used in this example; note also that the "caps" modifier indicates whether caps lock is active, not whether the key is physically pressed.