Monterey: Macscript detect Option (Alternate) key without Python?

I learned of this forum from RDB’s post back in 2014
https://macscripter.net/viewtopic.php?id=42931
subject “Alt, CMD, Ctrl or Shift key pressed”

DJ Bazzie Wazzie proposed a script that used the Cocoa lib in Python.
I used to be able to call this using the Macscript command in Excel 365 for Mac VBA.


do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags()' "

Unfortunately Python is dropped when a user upgrades to Monterey as my Mac Mini has just done.
My application is in Microsoft Office for Mac with Excel workbook VBA which uses OnKey macros.
My clients are end users who may not be comfortable with having to install a developer tool like Python3.

Is there a newer/better way to detect what modifier keys are being pressed?

Thank you

Hi. Welcome to MacScripter.

There’s AppleScriptObjC. For example:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on modifierKeysPressed()
	set |⌘| to current application
	set currentModifiers to |⌘|'s class "NSEvent"'s modifierFlags() --> Same as the Python result, but integer rather than text.
	
	set commandDown to (currentModifiers div (get |⌘|'s NSCommandKeyMask) mod 2 = 1)
	set optionDown to (currentModifiers div (get |⌘|'s NSAlternateKeyMask) mod 2 = 1)
	set controlDown to (currentModifiers div (get |⌘|'s NSShiftKeyMask) mod 2 = 1)
	set shiftDown to (currentModifiers div (get |⌘|'s NSControlKeyMask) mod 2 = 1)
	
	return {command_down:commandDown, option_down:optionDown, control_down:controlDown, shift_down:shiftDown}
end modifierKeysPressed

modifierKeysPressed() --> Record containing boolean values.

Thank you very much Nigel.
What is the command symbol inside pipes in that code? Can I replace it by a plain text variable name?
What I need is the modifierFlags integer value itself, so when I try it I’ll say “return currentmodifiers”

Patrick

Hi Patrick.

Indeed you can. The symbol in pipes is just a variable label I use to hold the ‘current application’ enum so that I don’t have to write it out at every point where it’s needed in a script. If you only want the modifierFlags integer, you need only write something like:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on modifierKeysPressed()
	return current application's class "NSEvent"'s modifierFlags()
end modifierKeysPressed

modifierKeysPressed() --> Record containing boolean values.

It doesn’t even have to be in its own handler if you don’t need that, but the ‘use’ declarations must be included at the top of the script.

Marvellous. Thank you. Now, is there a way to call the Applescript as a single string eg
Macscript(" use framework ““Foundation”” ; use etc ; return current application’s etc")
or do I use a return character instead of a semicolon, or is the only way to do that to create an applescript file?

I’m afraid I know nothing at all about how to do things in Office. If that’s the syntax, I’d guess use linefeeds or returns and escape the double-quotes.

Thanks Nigel. To execute from a file:

I found that I had to unhide the users library folder following these instructions:
https://www.macrumors.com/how-to/reveal-library-folder-in-macos/

Then create the script in Library/Application Scripts/com.microsoft.Excel

Then call it in VBA using this example sub. It takes about 0.7s to run so the user has to keep holding the modifier key down longer than feels natural.

Sub testAST()
Dim lFlags As Long, nTimer As Double
nTimer = Timer()
'/Users/minimac/Library/Application Scripts/com.microsoft.Excel
lFlags = AppleScriptTask(“modifierKeysPressed.scpt”, “modifierKeysPressed”, “”)
MsgBox “modifierFlags " & lFlags & " secs=” & Timer - nTimer
End Sub

With more testing I now find that I can call a script in a string, separating lines by CRLF.
and it only takes 0.05 seconds! This is the minimum script I ended up with:

Sub testMacScript()
Dim scpt As String, nTimer As Double
nTimer = Timer()
scpt = “use framework ““AppKit”””
scpt = scpt & vbCrLf & “return current application’s class ““NSEvent””'s modifierFlags()”
'Debug.Print MacScript(scpt)
MsgBox scpt & vbLf & MacScript(scpt) & " secs=" & Timer - nTimer
End Sub

Thanks, Patrick. I’ve just seen your edit.

Sorry. I did mean to mention that if getting the modifier flags was all the script was going to do, the use scripting additions line wouldn’t be necessary as there are no scripting addition commands in the code. Similarly, use AppleScript version “2.4” is just a nicety which gives a more informative error message if the running system’s too old to understand the code.

I think it’s possible for users to open their user Library folder from the Finder’s “Go” menu even when the folder’s hidden. But I always keep mine unhidden and have a log-in script which ensures this.

By the way, is there a way to get the original keyCode as well as the modifier key, in one Long Integer variable where the high bits are the modifiers (as at present) and the low bits the unicode key? I tried using .keyCode instead of .modifierKeys but that didn’t work. If I could follow the NSEvent doc better, I might have figured it out.
https://developer.apple.com/documentation/appkit/nsevent