#pragma once extern "C" { #include } #include #include "SCPatchCommon.h" #include "SCPatchMessages.h" #define DEFAULT_MESSAGE_RETRY_LIMIT 100 // Give up after 5 seconds #define DEFAULT_MESSAGE_RETRY_INTERVAL .05 // These AppleEvents get sent to ReceiveMessage after StartListening and // before StopListening, respectively. const OSType kEventClassSCPatchMessenger = 'SCPm'; const OSType kEventSCPatchStart = 'strt'; const OSType kEventSCPatchStop = 'stop'; typedef struct { Boolean threaded; UInt32 retryCount; Ptr descData; UInt32 descSize; CFStringRef portName; } AEMessageInfo; class SCPatchMessenger { public: SCPatchMessenger(void); virtual ~SCPatchMessenger(void); void SetMessageRetryInterval(float interval) { mMessageRetryInterval = interval; } float GetMessageRetryInterval(void) { return mMessageRetryInterval; } void SetMessageRetryLimit(UInt32 limit) { mMessageRetryLimit = limit; } UInt32 GetMessageRetryLimit(void) { return mMessageRetryLimit; } // Start listening for messages. If called from a patch, a PSN must be supplied. // When called from a controller, pass NULL for the PSN. OSErr StartListening(CFStringRef myBundleIdentifier, Boolean isPatch, Boolean threaded); OSErr StopListening(void); // When sending from controller to patch, you have to include the PSN to disambiguate // the bundleID (because the same bundle is loaded into multiple apps), but when // sending from patch back to controller, the PSN can be null, since there's only one // controller running. OSErr SendPing(CFStringRef bundleIdentifier, ProcessSerialNumber *psn); OSErr SendMessage(CFStringRef bundleIdentifier, ProcessSerialNumber *psn, AEEventClass evtClass, AEEventID evtID, const char *paramsFmt, ...); OSErr SendMessage(CFStringRef bundleIdentifier, ProcessSerialNumber *psn, const AppleEvent *theAE, Boolean threaded); // Override this to receive messages. virtual void ReceiveMessage(const AppleEvent *theAE) = 0; protected: virtual OSErr HandleMessage(const AppleEvent *) { return eventNotHandledErr; } private: // Info for the listening port CFMessagePortRef mMessagePort; CFRunLoopSourceRef mMessageSource; // Flags for whether we queue outgoing messages, and whether sending is threaded. Boolean mThreaded; // We have named send queues, where the key is the port name. // Each queue is a CFArray of messages. CFMutableDictionaryRef mMessageQueueDict; CFMutableDictionaryRef mMessageThreadDict; pthread_mutex_t mMessageQueueMutex; float mMessageRetryInterval; UInt32 mMessageRetryLimit; OSErr SendMessage(AEMessageInfo *info); static void *SendThreadEntryPoint(void *infoPtr); OSErr QueueMessage(AEMessageInfo *info); AEMessageInfo *GetNextMessage(pthread_t threadID); AEMessageInfo *DequeueMessage(pthread_t threadID); static CFDataRef ReceiveCallback(CFMessagePortRef local, SInt32 message, CFDataRef dataRef, void *info); };