Unfortunately, [[NSApp currentEvent] modiferFlags] can’t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.
The GetCurrentEventKeyModifiers() call works on Mac OS X v10.2 and later through which one can check for shiftKey, cmdKey, optionKey and controlKey.
I use the something like the following to check if the shift key is pressed during launch for my compression app.
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if((GetCurrentEventKeyModifiers() & shiftKey)!=0) {
// Change something useful
NSLog(@"Shift key pressed during launch");
}
[[NSApplication sharedApplication] run];
[pool release];
return 0;
}
In the highly unlikely event that you’ve just got a call to NSApplicationMain in your main function (joking), you can just test (GetCurrentEventKeyModifiers() & shiftKey)!=0 in the -applicationWillFinishLaunching: method in your NSApplication delegate.
Any new APIs in Leopard to achieve this or any easier way?
8 Comments so far
Leave a commentIt’s no surprise that -[NSApplication currentEvent] doesn’t work in main()–you haven’t started the event loop yet.
recorded by Scrod on November 28, 2007 3:41 am | Permalink
Scrod, I tried it in the application delegate methods such as
-applicationWillFinishLaunching:. No go.recorded by Ankur on November 28, 2007 7:28 am | Permalink
Without owning Leopard, I can almost certainly be sure that the function GetCurrentEventKeyModifiers() would be included in the list of 32-bit-only Carbon frameworks. I’m disappointed to see that Leopard hasn’t brought a Cocoa replacement.
I’m curious, what is the type of the current event? Is a key down event, some weird appkit event, or something else? Is it possibly nil?
My thoughts are, though, that testing for modifiers during launch is very OS 9-like, and relies on the idea that launching with no modifiers pressed as normal system behaviour.
We know that we can reveal a Dock app’s location by command-clicking, that when we launch/open something with the option-key down, it will often hide the formerly frontmost app. I don’t know of the shift key currently being used for anything, but you, of course, cannot rely on this always being the case in the future. You also cannot expect the user to suddenly change modifiers at the exact instance when you app is being launched - “Oops, our app launches in under 0.05s, we’ll just have to poll for 1 second before the app finishes launching!”.
(I hope none of that sounded angry, or directly aimed at you, Ankur
BTW, I hadn’t noticed The Zipper before; thanks. Can it deal with more than one operation at once? I’ve tried to drop a second file on while the first is still going, but it doesn’t seem to be picked up. Even if it does work, and I’m just stupid, I think this is a good example of the modifier-on-launch method being a bad idea, as you can’t have an already open app launch again. (I mean practically in user’s world, I know you can do it programmatically or through the command-line).
composed by Qwerty Denzel on November 28, 2007 4:45 pm | Permalink
Holding shift while you’re dragging, dropping the file(s), then releasing the shift key after the app’s started launching works; so that shouldn’t be a problem for the Zipper.
Ie, you can have shift pressed before the app’s launched as long as you hold it for a few milliseconds after it’s loaded.
But most of the time you don’t need to hold shift anyway - the zip format is only useful for sharing files with Windows users. Bzip2 is awesome.
You mean compressing multiple files? Just select the files you need and drop them onto the icon all at once.
Actually, OmniWeb uses shift on launch to create a new workspace. They have an item in their changelog: I think they’re doing what I did with the Zipper - checking for modifiers before the event loop starts.
divulged by Ankur on November 28, 2007 8:15 pm | Permalink
Yeah, but what I’m referring to is when those keys are used by the launcher. For instance, you can’t hold down the option-key without it doing the hide-the-front-app thing. What happens when Apple one day decides to add more? Holding down shift pre-launching might make the last app quit (or whatever), and then it can no longer functionally be used by the launching app (launchee?).
Also, what happens if the launching was performed using not the mouse, but the keyboard instead? Future system services (e.g. accessibility) might have a command for launching that uses the shift key.
(Note that I am not sure if these system function are perform automatically for you by LaunchServices, or if they are implemented specifically in the Finder and Dock. I have a feeling it might be the latter.)
I mean starting one operation with a batch of files, and then starting another, completely separate operation while the first is still going. This is where I then put forward: even if your app does do this, your key check won’t work for the second operation because the app is already launched and running. So, do you perform the key check in the -openFiles: NSApp delegate method instead?
I think Apple didn’t add a +modifierFlags NSEvent class method because they decided applications should only respond to events that are given to them officially, which is in the form of an NSEvent object. In a sense, events don’t ‘belong’ to an app until it is fully operational and is frontmost, otherwise they belong to the preceding app. In my minimal testing, -[NSEvent currentEvent] returns nil in -applicationWillFinishLaunching:, including when the shift key is being pressed pre-launch.
So… what are the alternatives? Having two separate apps doesn’t sound too enticing. And having a options dialog box popup everytime isn’t very streamlined at all. Any ideas?
divulged by Qwerty Denzel on November 29, 2007 8:53 am | Permalink
You’re right. But Omni does it. And if the system changes, we can modify the app to use a different modifier or something.
I made a QS trigger to launch the Zipper, and held shift a little longer than the other keys - created a zip archive as intended. Also my trigger for OmniWeb has the shift key in it, so occasionally it’ll do it’s new workspace thing without my approval. Annoying. But it doesn’t happen anymore.
I could do as you suggested by checking the modifiers in the
openFiles:method, but is it really necessary? It’s meant to be a simple app. The entire source code is smaller than our last few comments. Plus, it’s so fast that it’s done before you know it.If you want to do multiple operations, I’d say go with zsh.
for file in *; tar -cjf ${file:t:r}.tar.bz2 $filewill compress each file in the cwd to a tbz separately.reasonded by Ankur on November 29, 2007 9:18 am | Permalink
Thanks for the quick reply, Ankur. To be honest, I think I’ll only use Bzip2 too.
I think I remember at least one Apple OpenGL sample-code app that used a modifier key to hide or show a preferences dialog on launch.
Now… I’ve found an answer to your original post question.
10.4 introduced CGEvents, which is part of the CoreGraphics framework. You can do the following to check for the shift key:
For even earlier versions of OS X, you could probably use IOKit.
announced by Qwerty Denzel on November 29, 2007 10:03 am | Permalink
Thanks! I’ll try that.
determined by Ankur on November 29, 2007 11:06 am | Permalink
Leave a comment