#include "common.h" class PlatformThreadingInterface; @interface Signaler : NSObject -(void) setParent: (PlatformThreadingInterface*)parent; -(void) signal: (int) priority; -(Signaler*) init; @end @implementation ActionCallback { ComPtr _callback; } - (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback { _callback = callback; return self; } - (void) action { _callback->Run(); } @end class TimerWrapper : public ComUnknownObject { NSTimer* _timer; public: TimerWrapper(IAvnActionCallback* callback, int ms) { auto cb = [[ActionCallback alloc] initWithCallback:callback]; _timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(double)ms/1000 target:cb selector:@selector(action) userInfo:nullptr repeats:true]; } virtual ~TimerWrapper() { [_timer invalidate]; } }; class PlatformThreadingInterface : public ComSingleObject { private: Signaler* _signaler; bool _wasRunningAtLeastOnce = false; class LoopCancellation : public ComSingleObject { public: FORWARD_IUNKNOWN() bool Running = false; bool Cancelled = false; virtual void Cancel() override { Cancelled = true; if(Running) { Running = false; dispatch_async(dispatch_get_main_queue(), ^{ [[NSApplication sharedApplication] stop:nil]; NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [NSApp postEvent:event atStart:YES]; }); } } }; public: FORWARD_IUNKNOWN() ComPtr SignaledCallback; PlatformThreadingInterface() { _signaler = [Signaler new]; [_signaler setParent:this]; } ~PlatformThreadingInterface() { if(_signaler) [_signaler setParent: NULL]; _signaler = NULL; } virtual bool GetCurrentThreadIsLoopThread() override { return [NSThread isMainThread]; } virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override { SignaledCallback = cb; } virtual IAvnLoopCancellation* CreateLoopCancellation() override { return new LoopCancellation(); } virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) override { START_COM_CALL; auto can = dynamic_cast(cancel); if(can->Cancelled) return S_OK; if(_wasRunningAtLeastOnce) return E_FAIL; can->Running = true; _wasRunningAtLeastOnce = true; [NSApp run]; return S_OK; } virtual void Signal(int priority) override { [_signaler signal:priority]; } virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) override { @autoreleasepool { return new TimerWrapper(callback, ms); } } }; @implementation Signaler PlatformThreadingInterface* _parent = 0; bool _signaled = 0; NSArray* _modes; -(Signaler*) init { if(self = [super init]) { _modes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; } return self; } -(void) perform { ComPtr cb; @synchronized (self) { _signaled = false; if(_parent != NULL) cb = _parent->SignaledCallback; } if(cb != nullptr) cb->Signaled(0, false); } -(void) setParent:(PlatformThreadingInterface *)parent { @synchronized (self) { _parent = parent; } } -(void) signal: (int) priority { @synchronized (self) { if(_signaled) return; _signaled = true; [self performSelector:@selector(perform) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes:_modes]; } } @end extern IAvnPlatformThreadingInterface* CreatePlatformThreading() { return new PlatformThreadingInterface(); }