Detect Modifier Key down

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()

Hi KniazidisR.

I often do listen to Shane’s good advice. :wink: Otherwise the reasons I didn’t and won’t write code like that are given in my discussion with him above. Other people are free to make up their own minds.

In the case of the current handler, which is unlikely to be the only thing in a script, a better compromise would be to write the numbers directly into its code with comments explaining what they mean:

use AppleScript version "2.4" -- The ability to use ASObjC in running scripts was introduced in Mac OS 10.10.
use framework "Foundation"
use framework "AppKit" -- NSEvent is an AppKit class.
use scripting additions -- In case needed.

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
		-- 524288: NSAlternateKeyMask constant in Mac OS 10.10 & 10.11/NSEventModifierFlagOption thereafter.
		set its option_down to (currentModifiers div 524288 mod 2 is 1)
		-- 1048576: NSCommandKeyMask/NSEventModifierFlagCommand.
		set its command_down to (currentModifiers div 1048576 mod 2 is 1)
		-- 131072: NSShiftKeyMask/NSEventModifierFlagShift.
		set its shift_down to (currentModifiers div 131072 mod 2 is 1)
		-- 262144: NSControlKeyMask/NSEventModifierKeyControl.
		set its control_down to (currentModifiers div 262144 mod 2 is 1)
	end tell
	
	return modifierKeysDOWN
end modifierKeysPressed

modifierKeysPressed()

This way you dispense with ludicrous code like a reference to 524288 and with four pointless properties having their values saved back to the script file every time it’s run. At the same time, it’s immediately clear to anyone else trying to debug the larger script later what the numbers are supposed to mean and it won’t be necessary to look back through the script to see if or where NSEventModifierFlagOption has been set as a variable and whether or not its use without current application’s in front of it is deliberate or a typo.