Make my Cocoa app respond to the keyboard play/pause key? - objective-c

Is there a way to make my app respond to the play/pause button on Mac?
EDIT:
Using the suggested code,I get this console message:
Could not connect the action buttonPressed: to target of class NSApplication
Why would that be?

I accomplished this in my own application by subclassing NSApplication (and setting the app's principal class to this subclass). It catches seek and play/pause keys and translates them to specific actions in my app delegate.
Relevant lines:
#import <IOKit/hidsystem/ev_keymap.h>
- (void)sendEvent:(NSEvent *)event
{
// Catch media key events
if ([event type] == NSSystemDefined && [event subtype] == 8)
{
int keyCode = (([event data1] & 0xFFFF0000) >> 16);
int keyFlags = ([event data1] & 0x0000FFFF);
int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
// Process the media key event and return
[self mediaKeyEvent:keyCode state:keyState];
return;
}
// Continue on to super
[super sendEvent:event];
}
- (void)mediaKeyEvent:(int)key state:(BOOL)state
{
switch (key)
{
// Play pressed
case NX_KEYTYPE_PLAY:
if (state == NO)
[(TSAppController *)[self delegate] togglePlayPause:self];
break;
// Rewind
case NX_KEYTYPE_FAST:
if (state == YES)
[(TSAppController *)[self delegate] seekForward:self];
break;
// Previous
case NX_KEYTYPE_REWIND:
if (state == YES)
[(TSAppController *)[self delegate] seekBack:self];
break;
}
}
Edit:
Swift 4:
override func sendEvent(_ event: NSEvent)
{
if event.type == .systemDefined &&
event.subtype == .screenChanged
{
let keyCode : Int32 = (Int32((event.data1 & 0xFFFF0000) >> 16))
let keyFlags = (event.data1 & 0x0000FFFF)
let keyState = ((keyFlags & 0xFF00) >> 8) == 0xA
self.mediaKeyEvent(withKeyCode: keyCode, andState: keyState)
return
}
super.sendEvent(event)
}
private func mediaKeyEvent(withKeyCode keyCode : Int32, andState state : Bool)
{
guard let delegate = self.delegate as? AppDelegate else { return }
switch keyCode
{
// Play pressed
case NX_KEYTYPE_PLAY:
if state == false
{
delegate.musicPlayerWC.handleUserPressedPlayButton()
}
break
// Rewind
case NX_KEYTYPE_FAST:
if state == true
{
delegate.musicPlayerWC.handleUserPressedNextSongButton()
}
break
// Previous
case NX_KEYTYPE_REWIND:
if state == true
{
delegate.musicPlayerWC.handleUserPressedPreviousSongButton()
}
break
default:
break
}
}

Here's a great article on the subject: http://www.rogueamoeba.com/utm/2007/09/29/

Check this: https://gist.github.com/gauravk92/546311
Works perfectly.
As example, this repo uses it: https://github.com/onepill/PauseIt

In case anyone comes looking, there is sample code to do this here. This approach does allow you to "eat" the events they do not reach iTunes or other media-key-aware apps.
But be warned that event taps are not allowed by the sandbox, so this won't work in the App Store. If anyone has a workaround for that I'd love to hear it.

Related

MacOS (OSX) Dictation - Is there a way to read the shortcut keys assigned to dictation feature in Mac? or programmatically start dictation?

I want to enable dictation on any keyboard key press.
Currently I am using AppleScript to click on Menu->Edit->Start Dictation of front most app.
This script requires Automation permissions and I am hesitating to create app with so many permissions.
Is there a way to get the shortcut keys assigned to Dictation feature?
example some preference file in ~/Library/Preferences...
Goal is to:
Read shortcut keys from preference file
Fire those keys from app (CGEVENTPOST)
I ended up implementing myself. Captured changes made to Preferences folder after modifying any Keyboard setting.
Came across a file called com.apple.symbolichotkeys.plist which changed when any shortcuts were modified.
Here is sample code implemented to enable dictation programmatically (Hope my code is self explanatory)
#include <Carbon/Carbon.h>
[self enableDication];
-(void) enableDication
{
int press_count = 2;
NSString *pathSymbHotKeys = #"~/Library/Preferences/com.apple.symbolichotkeys.plist";
NSMutableDictionary *symbolicHotKeysDict = [[NSMutableDictionary alloc]initWithContentsOfFile:pathSymbHotKeys.stringByExpandingTildeInPath];
NSNumber *enabled = symbolicHotKeysDict[#"AppleSymbolicHotKeys"][#"164"][#"enabled"] ;
if (![enabled boolValue]) {
NSLog(#"Dication shortcut not enabled");
return;
}
NSString *type = symbolicHotKeysDict[#"AppleSymbolicHotKeys"][#"164"][#"value"][#"type"];
NSNumber *param_key_code = nil;
NSNumber *param_mask = nil;
if ([type isEqualToString:#"standard"]) {
//mask
param_mask = symbolicHotKeysDict[#"AppleSymbolicHotKeys"][#"164"][#"value"][#"parameters"][2];
param_key_code = symbolicHotKeysDict[#"AppleSymbolicHotKeys"][#"164"][#"value"][#"parameters"][1];
press_count = 1;
}
else {
param_key_code = symbolicHotKeysDict[#"AppleSymbolicHotKeys"][#"164"][#"value"][#"parameters"][0];
}
CGEventRef fnDown = CGEventCreateKeyboardEvent(NULL, [self keyCode:param_key_code type:type], true);
if ([type isEqualToString:#"standard"]) {
CGEventSetFlags(fnDown, (CGEventFlags)([param_mask intValue] | CGEventGetFlags(fnDown)));
}
CGEventRef fnUp = CGEventCreateKeyboardEvent(NULL, [self keyCode:param_key_code type:type], false);
if ([type isEqualToString:#"standard"]) {
CGEventSetFlags(fnUp, (CGEventFlags)([param_mask intValue]));
}
for (int i = 0; i <press_count; i++)
{
CGEventPost(kCGSessionEventTap, fnDown);
CGEventPost(kCGSessionEventTap, fnUp);
}
CFRelease (fnDown);
CFRelease (fnUp);
}
-(CGKeyCode) keyCode:(NSNumber *)code type:(NSString *)type
{
if ([type isEqualToString:#"standard"]) {
return (CGKeyCode)[code longValue];
}
else{
//Default Modifier shortcuts provided by Mac for dictation
if (([code longValue] & 0x00100008) == 0x00100008) {
//Left command key
return kVK_Command;
}
else if (([code longValue] & 0x00100010) == 0x00100010) {
// right command key
return kVK_RightCommand;
}
else if (([code longValue] & kCGEventFlagMaskCommand) == kCGEventFlagMaskCommand) {
//any command key
return kVK_Command;
}
else if (([code longValue] & NSEventModifierFlagFunction) == NSEventModifierFlagFunction)
{
//function key
return kVK_Function;
}
else if (([code longValue] & kCGEventFlagMaskControl) == kCGEventFlagMaskControl)
{
//control key
return kVK_Control;
}
}
return -1;
}

How Can I get keyboard input in a SpriteKit Game?

I'm a beginner in SpriteKit programming, and have been trying to figure out how to handle input from the keyboard.
What I've found so far is that you should subclass NSResponder and implement it like this:
#interface AppDelegate : NSResponder <NSApplicationDelegate>
-(void)keyUp:(NSEvent *)theEvent;
-(void)keyDown:(NSEvent *)theEvent;
#end
#implementation AppDelegate
-(void)keyUp:(NSEvent *)theEvent
{
NSLog(#"Key Released");
}
-(void)keyDown:(NSEvent *)theEvent
{
NSLog(#"Key Pressed");
}
#end
Obviously, there are a few more methods/properties in the interface and implementation of AppDelegate but I didn't put them there to keep the question relevant.
Next, I would start using key codes to detect which keys are being pressed, but the keyUp and keyDown methods don't even get called. I'm not sure why.
Any Help?
Update:
Thanks for your answers! I discovered that you have to implement keyUp and keyDown directly in your scene class, because they won't get called from the AppDelegate. Thanks again for the help!
The easiest way I know is to implement the keyDown method in your SKScene (and not directly in the AppDelegate). You don't have to subclass anything.
- (void)keyDown:(NSEvent *)event {
[self handleKeyEvent:event keyDown:YES];
}
- (void)keyUp:(NSEvent *)event {
[self handleKeyEvent:event keyDown:NO];
}
Then use the method handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp to check which key has been pressed :
- (void)handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp {
// First check the arrow keys since they are on the numeric keypad.
if ([event modifierFlags] & NSNumericPadKeyMask) { // arrow keys have this mask
NSString *theArrow = [event charactersIgnoringModifiers];
unichar keyChar = 0;
if ([theArrow length] == 1) {
keyChar = [theArrow characterAtIndex:0];
switch (keyChar) {
case NSUpArrowFunctionKey:
self.defaultPlayer.moveForward = downOrUp;
break;
case NSLeftArrowFunctionKey:
self.defaultPlayer.moveLeft = downOrUp;
break;
case NSRightArrowFunctionKey:
self.defaultPlayer.moveRight = downOrUp;
break;
case NSDownArrowFunctionKey:
self.defaultPlayer.moveBack = downOrUp;
break;
}
}
}
// Now check the rest of the keyboard
NSString *characters = [event characters];
for (int s = 0; s<[characters length]; s++) {
unichar character = [characters characterAtIndex:s];
switch (character) {
case 'w':
self.defaultPlayer.moveForward = downOrUp;
break;
case 'a':
self.defaultPlayer.moveLeft = downOrUp;
break;
case 'd':
self.defaultPlayer.moveRight = downOrUp;
break;
case 's':
self.defaultPlayer.moveBack = downOrUp;
break;
case ' ':
self.defaultPlayer.fireAction = downOrUp;
break;
}
}
}
I took this code from the Apple SpriteKit Adventure game. I found it very usefull to learn SpriteKit :)
Swift (2.0) version of HeyFara's answer. I've just popped "breaks" in where you would make your actual function calls.
public override func keyDown(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: true)
}
public override func keyUp(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: false)
}
public func handleKeyEvent(event:NSEvent, keyDown:Bool){
if event.modifierFlags.contains(NSEventModifierFlags.NumericPadKeyMask){
if let theArrow = event.charactersIgnoringModifiers, keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters.characters{
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
Here is rougeExciter version of HeyFara's answer but in Swift 4.2... in case someone out there find this old post... as myself a few minutes ago :)...
public override func keyDown(with event: NSEvent) {
handleKeyEvent(event, keyDown: true)
}
public override func keyUp(with event: NSEvent) {
handleKeyEvent(event, keyDown: false)
}
public func handleKeyEvent(_ event: NSEvent, keyDown: Bool){
if event.modifierFlags.contains(NSEvent.ModifierFlags.numericPad) {
if let theArrow = event.charactersIgnoringModifiers, let keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters {
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
If you want to know whenever a key is pressed, you should subscribe to the global event manager.
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event) {
[NSEvent removeMonitor:event];
This will call the handler when ever a key is pressed.

NSWindow keyDown: crashes

In my application, I inherited NSWindow class. This is to override default key down event.
//mywindow.h
#interface TWindow: NSWindow
{
}
- (void)keyDown: (NSEvent *)pEvent;
#end
//mywindow.mm
- (void)keyDown:(NSEvent *)theEvent
{
//Detect Control-W and Control-Q and handle them.
unsigned short keycode; ///< Key code.
NSString * charigmtch; ///< Character ignoring matches.
NSString * character;
BOOL cmdkeydown; ///< check if the command key is down.
BOOL shiftkeydown; ///< Check if shift key is down.
//Get the keycode of Control-W
keycode = [theEvent keyCode];
//get character ignoring match.
charigmtch = [theEvent charactersIgnoringModifiers];
//get character
character = [theEvent characters];
cmdkeydown = ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask;
shiftkeydown = ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) == NSShiftKeyMask;
//Get the keycode of Control
if(cmdkeydown && 12 == keycode && ![character compare:#"q"] && ![charigmtch compare:#"q"]) {
//CloseWithConfirm shows message box confirming quit.
[self CloseWithConfirm];
} else if (keycode == 48) {
//Tab key is pressed.
//This AppDelegate is application delegate and also NSWindowDelegate.
AppDelegate * delegate; ///< Delegate.
//Get the delegate from the window.
delegate = (AppDelegate *)[self delegate];
//Shift key is not down.
if(!shiftkeydown) {
//Tab key is pressed.
[delegate TabKeyPressed];
} else {
//Shift-Tab key is pressed.
[delegate ShiftTabKeyPressed];
}
}
//Handle the other key.
[super keyDown:theEvent]; //Line E
}
When I run following sample test case:
void TestCase ()
{
MyThread thread1, thread2, thread3, thread4;
//Run thread 1
thread1.Execute();
//Run thread 2
thread2.Execute();
//Run thread 3
thread3.Execute();
//Run thread 4
thread4.Execute();
}
//Following function is executed in thread function.
void Perform ()
{
//This adds the data in table view.
AddRowInTableView ("Test");
}
This code run fines most the time. But sometimes, it crashes at Line E marked in the code.
I am not able to find the reason of this crash. Can anyone help me?
I am using Mac OS X Mavericks preview and Xcode 5 for debugging and building.
Do not update UI in secondary thread. All drawing and UI events must be handled on the main thread.

Detecting Ctrl + Return or Ctrl + Enter presses

I find it very hard to find anything official on this matter.
I have a TextView and override the keyDown event and try to detect if the user pressed Ctrl + Enter.
- (void)keyDown:(NSEvent *)theEvent
{
if([theEvent modifierFlags] & NSControlKeyMask && /* how to check if enter is pressed??? */)
{
NSLog(#"keyDown: ctrl+enter");
if(_delegate)
{
if([_delegate respondsToSelector:#selector(didSomething:)])
{
[_delegate performSelector:#selector(didSomething:) withObject:nil];
}
}
}else
{
[super keyDown:theEvent];
}
}
I have tried different things but nothing worked.
anyone?
(i'm sorry to ask such a trivial question but i have googled for a while now and haven't found anything)
unichar c = [theEvent charactersIgnoringModifiers] characterAtIndex:0];
if(([theEvent modifierFlags] & NSControlKeyMask) && (c == NSCarriageReturnCharacter || c == NSEnterCharacter) {
// do stuff
}
Alternatively, you can use the delegate's textView:doCommandBySelector::
- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector {
// insertLineBreak: is the method sent when a user presses control-return in a text view
if (aSelector == #selector(insertLineBreak:)) {
// do stuff
return YES;
}
}
return NO;
}
Instead of overriding -keyDown:, you could override the keyboard action (insert a line break) that’s sent when ctrl-return is typed in a text view:
- (void)insertLineBreak:(id)sender {
NSLog(#"ctrl-return");
if(_delegate)
{
…
}
}

Anyway to make a (wrapping) NSTextField write a carriage return upon pressing return key?

I want to use a wrapping text field that can potentially contain carriage returns in my app. Is there any way to force the NSTextField object to write a carriage return into the text area instead of sending its action to its target when the Return key is pressed?
This is covered in Technical Q&A QA1454, which also enumerates reasons why one would use NSTextField instead of NSTextView in this case.
You can implement the following method in the text field delegate:
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver
// to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
Okay, I figured out one way to do it, but this very well may not be the best (or even a good) way. I subclassed NSTextField, and overrode -textShouldEndEditing: like so:
-(BOOL)textShouldEndEditing:(NSText *)textObject {
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36) {
[self setStringValue:[[self stringValue] stringByAppendingString:#"\n"]];
return NO;
}
else {
return [super textShouldEndEditing:textObject];
}
}
I found a combination of Sean and Bevarious worked best for me. Sean's answer assumes that the new line is always wanted to be added to the end (instead of for instance where the user's cursor is placed).
-(BOOL)textShouldEndEditing:(NSText *)textObject
{
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36)
{
[textObject insertNewlineIgnoringFieldEditor:nil];
return NO;
}
else
{
return [super textShouldEndEditing:textObject];
}
}
Swift version:
override func textShouldEndEditing(textObject: NSText) -> Bool {
let event = NSApplication.sharedApplication().currentEvent
if event?.type == NSEventType.KeyDown && event?.keyCode == 36 {
self.stringValue = self.stringValue.stringByAppendingString("\n")
return false
} else {
return super.textShouldEndEditing(textObject)
}
}