Detect Modifier Key down

I was reading through a forum post elsewhere and came upon the app this uses. I took a basic script an tested it for options and thought it may be of use to some.
It detects a modifier key (Option, Shift, etc.) down. Can be set to only detect a specific key or to report any modifier key.
(modified to include control key and explain the results better in the comments so it is self contained mostly)



------ Depends on getModKey app from Allan Craig 
--- http://allancraig.net/index.php?option=com_docman&Itemid=100
-- run this in an idle handler to get keys down or in some other fashion to capture a key down when the do shell executes
-- if you append to the quoted text at the end of the 'do shell' line a space and then the mod key name (such as 'option'), it will provide a boolean result on the status of the particular mod key state
-- I have provided several keys and combinations below, notice that they are additive in nature (see the combinations with the Option key)
-- I have the log message so that you can see the numeric value of the result returned, either the key value or the boolean

set ModKeyDown to my ModKeyDownCheck()
-----
set ModKeyAlert to ""
if ModKeyDown > 1 then
	if ModKeyDown = 256 then
		set ModKeyAlert to "Command key pressed"
	else if ModKeyDown = 512 then
		set ModKeyAlert to "Shift key pressed"
	else if ModKeyDown = 1024 then
		set ModKeyAlert to "Caps Lock on"
	else if ModKeyDown = 2048 then
		set ModKeyAlert to "Option key pressed"
	else if ModKeyDown = 2560 then -- note the key combinations cause addition of their values
		set ModKeyAlert to "Shift & Option keys pressed"
	else if ModKeyDown = 2816 then
		set ModKeyAlert to "Shift, Option & Command keys pressed"
	else if ModKeyDown = 3072 then
		set ModKeyAlert to "Caps Lock on & Option key pressed"
	else if ModKeyDown = 4096 then
		set ModKeyAlert to "Control key pressed"
	else if ModKeyDown = 4352 then
		set ModKeyAlert to "Control and Command keys pressed"
	else if ModKeyDown = 131072 then
		set ModKeyAlert to "Function key pressed"
	else if ModKeyDown = 133120 then
		set ModKeyAlert to "Function & Option keys pressed"
	end if
end if
-----
if ModKeyAlert ≠ "" then
	display alert ModKeyAlert
end if
-----
on ModKeyDownCheck()
	set ModKeyDown to do shell script "/Library/UnixApps/getModKey"
	(* use this if you wish only to monitor for Option key down:
	set ModKeyDown to do shell script "/Library/UnixApps/getModKey option"
	*)
	set ModKeyDown to ModKeyDown as integer
	log ModKeyDown
	-- set ModKeyDown to modKeyDown as boolean
	return ModKeyDown
end ModKeyDownCheck
-----

Model: MacBook (early 2009)
Browser: Safari 533.16
Operating System: Mac OS X (10.6)

Hello.

I just want to add that StefanK has made an alternative version that I find easier to use since you can specify the keys you want to listen to on the command line, and gets a true result if that condition of the keys were true at that moment. I’m not saying that the one is better than the other, just that there is an alternative. The version you describe is the obvious choice when listening for function keys. But can you get out which function key was pressed?

[code]checkModifierKeys(1) BSD General Commands Manual checkModifierKeys(1)

NAME
checkModifierKeys, – Modifier Key listener for the Mac Os X

SYNOPSIS
checkModifierKeys, [shift | cmd | control | option | capslock]

DESCRIPTION
checkModifierKeys, Returns “1” to standard output if one of or more of
the modifierkeys in the argumentlist were held down under execution or
“0” if not.

AUTHOR
Stefan Klieme

Darwin July 22, 2010 Darwin[/code]
You can download StefanK’s checkModifierKeys from here

Thanks.
Yes, with the method I listed one can get the specific mod key pressed, including key combinations, if the button parameter is left off of the do shell command (is shown this way in the script without he param option commented out).
I check out Stefan’s method as well.

Hello.

The method you mention has great advantages, when you want to poll for different key combinations, as you will get by with one single do shell script command. That may be an advantage in some situations.

I checked out your alternative as well. Do you happen to know where I find the opcodes for the keys?
I really need the combinations for the control key as well.

Thanks in advance.

I have updated to include control (I ran it when the control key was down and looked in the log for the result). I did not know the opcodes originally, just acquired them through logging results as the script ran.

I have improved the comments to make it more easily understood (I hope).

The script is mostly the comments and the dialog to display the results of the do shell command. Otherwise it would be quite small.

Hello.

That didn’t cross my mind at the moment. :slight_smile: Thanks!

I re found the utility keycodes at Peter Maurers site (download link).
So I can easily test for the combinations I am interested in. -But I like the AS EventLog. :slight_smile:

Thanks! Very useful form som of my applets. :slight_smile:

This should also tell you they modifier keys that are down, using “vanilla” Applescript


property vers : "1.0"

on isModifierKeyPressed(checkKey)
	set modiferKeysDOWN to {command_down:false, option_down:false, control_down:false, shift_down:false, caps_down:false, numlock_down:false, function_down:false}
	
	if checkKey = "" or checkKey = "option" or checkKey = "alt" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSAlternateKeyMask '") > 1 then
			set option_down of modiferKeysDOWN to true
		end if
	end if
	
	if checkKey = "" or checkKey = "command" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSCommandKeyMask '") > 1 then
			set command_down of modiferKeysDOWN to true
		end if
	end if
	
	if checkKey = "" or checkKey = "shift" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSShiftKeyMask '") > 1 then
			set shift_down of modiferKeysDOWN to true
		end if
	end if
	
	if checkKey = "" or checkKey = "control" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSControlKeyMask '") > 1 then
			set control_down of modiferKeysDOWN to true
		end if
	end if
	
	if checkKey = "" or checkKey = "caps" or checkKey = "capslock" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSAlphaShiftKeyMask '") > 1 then
			set caps_down of modiferKeysDOWN to true
		end if
	end if
	
	if checkKey = "" or checkKey = "numlock" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSNumericPadKeyMask'") > 1 then
			set numlock_down of modiferKeysDOWN to true
		end if
	end if
	--Set if any key in the numeric keypad is pressed. The numeric keypad is generally on the right side of the keyboard. This is also set if any of the arrow keys are pressed
	
	if checkKey = "" or checkKey = "function" or checkKey = "func" or checkKey = "fn" then
		if (do shell script "/usr/bin/python -c 'import Cocoa; print Cocoa.NSEvent.modifierFlags() & Cocoa.NSFunctionKeyMask'") > 1 then
			set function_down of modiferKeysDOWN to true
		end if
	end if
	--Set if any function key is pressed. The function keys include the F keys at the top of most keyboards (F1, F2, and so on) and the navigation keys in the center of most keyboards (Help, Forward Delete, Home, End, Page Up, Page Down, and the arrow keys)
	
	return modiferKeysDOWN
end isModifierKeyPressed


You could call this by doing
my isModifierKeyPressed(“”), and this will return all current modifier keys pulled as a record.

You can also specify which key to look at, which runs faster.
my isModifierKeyPressed(“option”)

So, an example:

display dialog command_down of my isModifierKeyPressed(“command”)
This would show true if the command key is being held down.

if you specify a key, all others will return as false.

EDIT:
Updated code, and examples

I don’t know if I’d call Python-invoking shell scripts “vanilla AppleScript”, but that’s very handy code. Thanks. :slight_smile:

The Python stuff is recognisably just making system calls, so I suppose the same thing could be achieved with less overhead using ASObjC:

use framework "Foundation"
use framework "AppKit"

on modifierKeysPressed()
	set modifierKeysDOWN to {command_down:false, option_down:false, control_down:false, shift_down:false}
	
	set |⌘| to current application
	set currentModifiers to |⌘|'s class "NSEvent"'s modifierFlags()
	
	tell modifierKeysDOWN
		set its option_down to (currentModifiers div (get |⌘|'s NSAlternateKeyMask) mod 2 is 1)
		set its command_down to (currentModifiers div (get |⌘|'s NSCommandKeyMask) mod 2 is 1)
		set its shift_down to (currentModifiers div (get |⌘|'s NSShiftKeyMask) mod 2 is 1)
		set its control_down to (currentModifiers div (get |⌘|'s NSControlKeyMask) mod 2 is 1)
	end tell
	
	return modifierKeysDOWN
end modifierKeysPressed

modifierKeysPressed()

Hello Nigel,

This script has proven incredibly helpful - thank you very much!

It led me to search this past weekend for another type of script, but to no avail.

Perhaps you know:

Is it possible, again using ASObjC in the same manner as your script, to detect and identify ANY keypress?

I’d like to:

(1) detect keypresses (including modifiers) specifically
(2) override the keys pressed —
(3) including (probably especially) any accompanying modifiers — to trigger scripts that do specific functions in specific software

[e.g., CTRL-1 triggers script A, COMMAND+OPTION-M triggers script B, etc.].

– or any one of these steps?

The if / thens and the triggering of scripts I am familiar with; but not so the detecting, identifying and overriding of keypresses in ASObjC.

I have a lot of scripts that help me out with some professional software; and I can of course use keyboard shortcuts in Mac, or other software like Keyboard Maestro, but I’m interested in doing this myself — and am having a difficult time finding the answer.

Thank you again for this very helpful script,

Garrard.

Model: Mac Pro (Mid 2012) 3.46 GHz 6-Core Intel
Browser: Chrome
Operating System: macOS 10.13

Hi Garrard. Welcome to MacScripter.

Looking at the Xcode 10.0 documentation today, I couldn’t see why my ASObjC script, derived from oscar’s python code, was actually working! It uses modifierKeys as a class method of NSEvent, but the documentation doesn’t explicitly indicate that it is one. Fortunately, the Xcode 8.2.6 documentation on my older machine does explicitly show that, so I’m now happier about the script. :slight_smile:

According to Xcode 10.0, the modifier flags used in the script are deprecated as from macOS 10.13. We’re now supposed to use flags that were introduced in macOS 10.12. These new flags have the same values as the old, just much longer labels, so I don’t see the point. But hey. The script should continue to work for a while as given above, but to hedge one’s bets:

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

on modifierKeysPressed()
	considering numeric strings
		set SierraOrLater to (system version of (system info) > "10.11.6")
	end considering
	
	set modifierKeysDOWN to {command_down:false, option_down:false, control_down:false, shift_down:false}
	
	set |⌘| to current application
	set currentModifiers to |⌘|'s class "NSEvent"'s modifierFlags()
	
	if (SierraOrLater) then
		-- Modifier flags introduced in macOS 10.12 (Sierra).
		tell modifierKeysDOWN
			set its option_down to (currentModifiers div (get |⌘|'s NSEventModifierFlagOption) mod 2 is 1)
			set its command_down to (currentModifiers div (get |⌘|'s NSEventModifierFlagCommand) mod 2 is 1)
			set its shift_down to (currentModifiers div (get |⌘|'s NSEventModifierFlagShift) mod 2 is 1)
			set its control_down to (currentModifiers div (get |⌘|'s NSEventModifierFlagControl) mod 2 is 1)
		end tell
	else
		-- Modifier flags deprecated as from macOS 10.13 (High Sierra).
		tell modifierKeysDOWN
			set its option_down to (currentModifiers div (get |⌘|'s NSAlternateKeyMask) mod 2 is 1)
			set its command_down to (currentModifiers div (get |⌘|'s NSCommandKeyMask) mod 2 is 1)
			set its shift_down to (currentModifiers div (get |⌘|'s NSShiftKeyMask) mod 2 is 1)
			set its control_down to (currentModifiers div (get |⌘|'s NSControlKeyMask) mod 2 is 1)
		end tell
	end if
	
	return modifierKeysDOWN
end modifierKeysPressed

modifierKeysPressed()

It looks as if other key event information has to be extracted from individual events taken from a queue, but I can’t immediately see how to get hold of them. :confused:

Great to know that this needs to be updated – thank you so much again.

After learning AS over the last year, I see the limitations and advantages of it in conjunction with shell scripts making numerous calculations over very large arrays (lists).

Sometimes the shell calls (including grep, sed) slow down the process, and have needed to be replaced with clunky, longer AppleScript calculations, that just simply work faster over large, numerous repeat loops.

So peeking into ASObjC - introduced to me with this script - and seeing how easily and quickly this simple script works at doing something arguably complex from the AS point of view, gave me hope that there might ways to simplify the more interesting task of detecting, identifying & overriding key strokes.

Perhaps there is a way through ASObjC – I do see that doing so through Cocoa is an avenue; but I’ll need to start from scratch with the language (I’ve already followed an online instructional video to create a simple Hello World sample).

So part of this is impatience - but also curiosity and challenge.

Thank you for the quick reply, and beyond that, updated info.

G.

The change is essentially to make them easier to use from Swift. Lots of enums have been similarly renamed in each of the past few OS revisions, and more are possible.

The old versions aren’t likely to disappear any time soon. But if you want to future-proof — and past-proof — code, it’s best to define properties with actual numerical values (which will never change). That way, the term is essentially a placeholder (which is what it is, in effect, in Objective-C).

So something like:

property NSEventModifierFlagCommand : 1048576
-- or:
property NSCommandKeyMask : 1048576

This has the added bonus of eliminating current application’s.

Script Debugger will add them for you automatically if you use code-completion, like this:

-- classes, constants, and enums used
property NSEventModifierFlagCommand : a reference to 1048576
property NSCommandKeyMask : a reference to 1048576

The only down-side is that it makes cutting and pasting a bit trickier.

They’ve introduced class properties, of which modifierKeys is one. Like instance properties, you just treat it like a method.

Thanks for the info, Shane.

No script of mine will ever contain ‘a reference to’ a number! :wink:

That’s a pity. It’s quite a reasonable way of solving the copy-and-paste problem.

Hi Shane. I’m afraid I don’t follow.

What copy-and-paste problem?

In post #13, you say that properties set to references to numbers make cutting and pasting a bit trickier; in post #16, you say they’re a reasonable way of solving the copy-and-paste problem.

You’ve lost me. :confused:

Suppose you have a long-ish script, and you want to use a section of it in another script. If that section defines Cocoa terminology via properties, it can get difficult: you need to copy and paste the code, work out which properties are used, and then copy and paste them too.

If you use Script Debugger’s code completion with Using Properties for Cocoa Terms selected, enums get entered as properties with their numerical values, but with a reference to. Cocoa classes and constants also use a reference to, but in those cases it serves a direct purpose.

But with enums it serves an indirect purpose: it acts as a flag that the property is, in fact, an enum, and not just some simple integer property your script uses (as you suggest, there can be no other logical reason). That can be visually useful, but it can also be taken advantage of when copying.

So if we go back to the example of copying a section of a script, if you choose Edit → AppleScriptObjC → Copy as Standalone Code, Script Debugger can work out which variables in the selection are defined as Cocoa terms (integer properties without a a reference to can be ignored, for starters), and expand the variables in-line so that the code on the clipboard includes the required instances of current application’s inline. That means one (sort of) copy, and one paste.

(The in-lining isn’t always perfect, but it covers most bases.)

Script Debugger could have used some other convention, but I think this one is pretty good: it’s consistent with other Cocoa term declarations, it’s harmless, and it’s unlikely to result in false positives. Although we clearly forgot to factor in your sensitivities :wink:

In fact, much of the code I post here and elsewhere is written with Cocoa terms as properties, but I use Copy as Standalone Code for snippets to post, to keep it simple.

Thanks, Shane. I understand what you were saying now.

‘ :lol: ’

You wouldn’t have had too, since I’ve yet to use SD’s “Use Properties for Cocoa Terms”. :wink: I’ve generally gone for local variables set a bit closer to where they’re needed.

Hi, Nigel.

First, many thanks for the practical script. Now, Shane suggested to you use references to numerical values instead of variables that have different labels in old and new OSs.

Since the numerical values have remained unchanged, why not listen to Shane’s good advice and offer users a universal script - without any OS checks?


use framework "Foundation"

property NSEventModifierFlagOption : a reference to 524288
property NSEventModifierFlagCommand : a reference to 1048576
property NSEventModifierFlagShift : a reference to 131072
property NSEventModifierFlagControl : a reference to 262144

on modifierKeysPressed()
	set modifierKeysDOWN to {command_down:false, option_down:false, control_down:false, shift_down:false}
	set currentModifiers to current application's class "NSEvent"'s modifierFlags()
	tell modifierKeysDOWN
		set its option_down to (currentModifiers div (get NSEventModifierFlagOption) mod 2 is 1)
		set its command_down to (currentModifiers div (get NSEventModifierFlagCommand) mod 2 is 1)
		set its shift_down to (currentModifiers div (get NSEventModifierFlagShift) mod 2 is 1)
		set its control_down to (currentModifiers div (get NSEventModifierFlagControl) mod 2 is 1)
	end tell
	return modifierKeysDOWN
end modifierKeysPressed

modifierKeysPressed()