2

What I want:

I have a program running. When the program is in the tray and out of focus, I want to have a couple of global shortcuts set up to send messages to the program. What do I mean by "send messages"? Well, inside my program, all I want is to have an access to some flag, which would indicate the state the specified key-pair (fired or not). I would poll the flag in the loop and take a decision from there.

What I found:

System-wide hotkey for an application

system wide shortcut for Mac OS X

What I do not understand:

From the links above it looks like I have to pass a handler when registering a hotkey. On a hotkey press, OS calls the handler. It that right? What I do not understand is how in the world the system would call a handler inside my program if my program is running.

Community
  • 1
  • 1
user3496846
  • 1,627
  • 3
  • 16
  • 28

1 Answers1

2

I think your main problem is that you do not understand how Mac programming was done in the days before objective C and Cocoa became the norm. Before that, most programming was done in C (or C++) using Carbon. This name was used for a library that was supposed to be a "carbon" copy of a more modern set of APIs during the transition between Mac OS (Classic) and Mac OS X.

Another thing you have to understand is that the registration of hotkeys as given in the examples you give above must be paired by a registration of a Carbon Event handler that will be invoked when you hit that hotkey combination.

That said, I think you should read this legacy document about the Carbon Event Manager:

https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/Carbon_Event_Manager/CarbonEvents.pdf

And pay particular attention to how Carbon Events are supposed to be registered. I particularly use:

OSStatus  InstallEventHandler(EventTargetRef target,
                              EventHandlerUPP handlerProc,
                              UInt32 numTypes,
                              const EventTypeSpec* typeList,
                              void* userData,
                              EventHandlerRef* handlerRef);

The way I use it is that I made an objective C wrapper in which I basically do the following:

This is a part of a class, let's call it MyOwnEventHandler:

- (EventHandlerRef)handlerRef {

    if ( handlerRef == nil ) {
       NSAssert( InstallEventHandler(GetApplicationEventTarget(),
                                     &EventHandler,
                                      0,
                                      nil,
                                      self,
                                      &handlerRef ) == noErr, @"handlerRef" );
    }

    return handlerRef;
  }

  // this is a Carbon callback that the OS invokes when your app gets
  // a hotkey event that must be handled by you
  OSStatus EventHandler( EventHandlerCallRef inHandler,
                         EventRef inEvent,
                         void* inUserData )
 {
     EventHotKeyID hotKeyID;
     GetEventParameter( inEvent,
                        kEventParamDirectObject,
                        typeEventHotKeyID,
                        nil,
                        sizeof(EventHotKeyID),
                        nil,
                        &hotKeyID );

   // use this to get your MyOwnEventHandler object back if need be
   // the reason why we get this is because we passed self in InstallEventHandler
   // in Carbon event callbacks you cannot access self directly
   // because this is a C callback, not an objective C method
    MyOwnEventHandler* handler = (MyOwnEventHandler *)inUserData;

   // handle the hotkey here - I usually store the id of the EventHotKeyID struct
   // in a objective C hotkey object to look up events in an array of registered hotkeys
        
  return eventNotHandledErr; // return this error for other handlers to handle this event as well
}

// call this objective C wrapper method to register your Carbon Event handler
- (void)registerForGettingHotKeyEvents {
       const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard,   kEventHotKeyPressed }};
       AddEventTypesToHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
    }

// call this objective C wrapper method to unregister your Carbon Event handler
- (void)unregisterFromGettingHotKeyEvents {
       const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard,   kEventHotKeyPressed }};
       RemoveEventTypesFromHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
    }

I hope this helps. If you are stuck somewhere let me know and I will try to help you.

boxed
  • 3,895
  • 2
  • 24
  • 26
jvarela
  • 3,744
  • 1
  • 22
  • 43
  • thats quite surprising for me that c++ was used, because I am actually writing a c++ program now :) Does it mean that I can directly invoke necessary methods? And the thing is: I have my own main loop. In fact, I have an SMFL window which I have to support. But it does not do any work when the program is out of focus (it becomes invisible). Can I still embed Carbon Event Manager somewhere in there? Maybe when the program becomes invisible? Thanks a lot for your answer! – user3496846 Jan 19 '16 at 23:35
  • Now the Carbon Event manager comes in the Carbon.Framework. So add this framework and include the necessary headers by adding #include – jvarela Jan 21 '16 at 04:24