diff --git a/dub.sdl b/dub.sdl index 5abd12e..2217d0c 100644 --- a/dub.sdl +++ b/dub.sdl @@ -23,6 +23,12 @@ configuration "epoll-gaia" { versions "EventcoreEpollDriver" } +configuration "cfrunloop" { + platforms "osx" + versions "EventcoreCFRunLoopDriver" + lflags "-framework" "CoreFoundation" +} + configuration "kqueue" { platforms "osx" "freebsd" versions "EventcoreKqueueDriver" diff --git a/source/eventcore/core.d b/source/eventcore/core.d index 44d8754..c1447ba 100644 --- a/source/eventcore/core.d +++ b/source/eventcore/core.d @@ -2,14 +2,16 @@ module eventcore.core; public import eventcore.driver; -import eventcore.drivers.posix.loop.select; +import eventcore.drivers.posix.loop.cfrunloop; import eventcore.drivers.posix.loop.epoll; import eventcore.drivers.posix.loop.kqueue; +import eventcore.drivers.posix.loop.select; import eventcore.drivers.libasync; import eventcore.drivers.winapi.driver; import eventcore.internal.utils : mallocT, freeT; version (EventcoreEpollDriver) alias NativeEventDriver = EpollEventDriver; +else version (EventcoreCFRunLoopDriver) alias NativeEventDriver = CFRunLoopEventDriver; else version (EventcoreKqueueDriver) alias NativeEventDriver = KqueueEventDriver; else version (EventcoreWinAPIDriver) alias NativeEventDriver = WinAPIEventDriver; else version (EventcoreLibasyncDriver) alias NativeEventDriver = LibasyncEventDriver; diff --git a/source/eventcore/drivers/posix/loop/cfrunloop.d b/source/eventcore/drivers/posix/loop/cfrunloop.d new file mode 100644 index 0000000..9dca204 --- /dev/null +++ b/source/eventcore/drivers/posix/loop/cfrunloop.d @@ -0,0 +1,65 @@ +/** + `CFRunLoop` based event loop for macOS UI compatible operation. +*/ +module eventcore.drivers.posix.loop.cfrunloop; +@safe: /*@nogc:*/ nothrow: + +version (EventcoreCFRunLoopDriver): + +import eventcore.drivers.posix.loop.kqueue; +import eventcore.internal.corefoundation; +import eventcore.internal.utils; +import core.time; + + +alias CFRunLoopEventDriver = PosixEventDriver!CFRunLoopEventLoop; + +final class CFRunLoopEventLoop : KqueueEventLoopBase { +@safe nothrow: + private { + CFFileDescriptorRef m_kqueueDescriptor; + CFRunLoopSourceRef m_kqueueSource; + } + + this() + @trusted @nogc { + super(); + + CFFileDescriptorContext ctx; + ctx.info = cast(void*)this; + + m_kqueueDescriptor = CFFileDescriptorCreate(kCFAllocatorDefault, + m_queue, false, &processKqueue, &ctx); + + CFFileDescriptorEnableCallBacks(m_kqueueDescriptor, CFOptionFlags.kCFFileDescriptorReadCallBack); + m_kqueueSource = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, m_kqueueDescriptor, 0); + CFRunLoopAddSource(CFRunLoopGetMain(), m_kqueueSource, kCFRunLoopDefaultMode); + } + + override bool doProcessEvents(Duration timeout) + @trusted { + // submit changes and process pending events + auto kres = doProcessEventsBase(0.seconds); + + CFTimeInterval to = kres ? 0.0 : 1e-7 * timeout.total!"hnsecs"; + auto res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, to, true); + return kres || res == CFRunLoopRunResult.kCFRunLoopRunHandledSource; + } + + override void dispose() + { + () @trusted { + CFRelease(m_kqueueSource); + CFRelease(m_kqueueDescriptor); + } (); + super.dispose(); + } + + private static extern(C) void processKqueue(CFFileDescriptorRef fdref, + CFOptionFlags callBackTypes, void* info) + { + auto this_ = () @trusted { return cast(CFRunLoopEventLoop)info; } (); + auto res = this_.doProcessEventsBase(0.seconds); + () @trusted { CFFileDescriptorEnableCallBacks(this_.m_kqueueDescriptor, CFOptionFlags.kCFFileDescriptorReadCallBack); } (); + } +} diff --git a/source/eventcore/internal/corefoundation.d b/source/eventcore/internal/corefoundation.d new file mode 100644 index 0000000..5545da0 --- /dev/null +++ b/source/eventcore/internal/corefoundation.d @@ -0,0 +1,35 @@ +module eventcore.internal.corefoundation; + +version (Darwin): + +extern(C): + +static if (!is(typeof(CFRelease))) { + alias CFTypeRef = const(void)*; + alias CFTypeRef CFAllocatorRef; + extern const CFAllocatorRef kCFAllocatorDefault; + CFTypeRef CFRetain(CFTypeRef cf); + void CFRelease(CFTypeRef cf); +} + +static if (!is(typeof(CFRunLoop))) { + alias CFRunLoopMode = CFStringRef; + struct __CFRunLoop; + alias CFRunLoopRef = __CFRunLoop*; + struct __CFRunLoopSource; + alias CFRunLoopSourceRef = __CFRunLoopSource*; + + alias CFTimeInterval = double; + alias Boolean = bool; + + extern const CFStringRef kCFRunLoopDefaultMode; + extern const CFStringRef kCFRunLoopCommonModes; + + void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode); + CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled); +} + +static if (!is(CFFileDescriptor)) { + alias FSEventStreamRef = x; + FSEventStreamRef FSEventStreamCreate(CFAllocatorRef allocator, FSEventStreamCallback callback, FSEventStreamContext *context, CFArrayRef pathsToWatch, FSEventStreamEventId sinceWhen, CFTimeInterval latency, FSEventStreamCreateFlags flags); +}