Search code examples
objective-ccocoamacoseventskeyboard

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


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?


Solution

  • 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
        }
    
    }