2

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 & 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

0

Well, hard to prove a negative but, as the question you linked to yourself says: you are holding the and keys down when calling the script. It can not ignore them. Apple script is scripting, and much closer to GUI scripting than to applicative scripting.

I cannot offer any other idea than in the answer to the question you sent: just add a slight delay so that you release the keys before the keystrokes are sent.

delay 0.2
| improve this answer | |
  • So there's no way to override the keyboard for lifting the key? Because key down DOES override the keyboard; as far as I could tell, the only way to undo an applescript key down command is a key up command. The real problem is that, if I accidentally hold the key down 0.1 seconds too long, the simple series of harmless ascii characters get transformed into extremely destructive quit / close / save etc. commands – keflavich Jan 12 '12 at 22:08
  • @keflavich Well, key down does not so much override as complete the current keyboard state… – MattiSG Jan 13 '12 at 7:44
1

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.

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

    #include <Carbon/Carbon.h>
    
    // 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 < modifierKeyCount; i++) { // Loop through all possible modifier keys again, and print the label.
            if (current_modifier_keys & 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 < key_labels_length; i++) {
            if (0 == strcasecmp(arg_string, key_labels[i])) {
                return 1;
            }
        }
        return 0;
    }
    
  2. Compile the program by running the following commands from Terminal:

    cd ~/Desktop
    gcc -framework Carbon getModifierKeys.c -o getModifierKeys
    
  3. 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.

  1. 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
    
  2. 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.

| improve this answer | |
0

FastScripts pauses running scripts before keystroke and key code commands if modifier keys have not been released.

One cool trick in 2.6.1 is the way FastScripts behaves when your scripts include “keystroke” commands to synthesize keyboard presses. In the past, these scripts were tricky to get right in FastScripts, because the synthesized keystroke would be mixed up with the very keys you had used to invoke the script. Now, FastScripts will suspend execution of any such script until you release the keys that were pressed to invoke the script.

FastScripts does not support triggering scripts with pointing device actions, but you could use KeyRemap4MacBook to map pointing device actions to some unused key combinations first.

| improve this answer | |

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .