#ifndef _WX_OSX_EVTLOOPSRC_H_
#define _WX_OSX_EVTLOOPSRC_H_
-typedef struct __CFFileDescriptor *CFFileDescriptorRef;
+typedef struct __CFSocket* CFSocketRef;
// ----------------------------------------------------------------------------
// wxCFEventLoopSource: CoreFoundation-based wxEventLoopSource for OS X
class WXDLLIMPEXP_BASE wxCFEventLoopSource : public wxEventLoopSource
{
public:
+ // Create a new source in uninitialized state, call InitSocketRef() later
+ // to associate it with the socket it is going to use.
wxCFEventLoopSource(wxEventLoopSourceHandler *handler, int flags)
: wxEventLoopSource(handler, flags)
{
- m_cffd = NULL;
+ m_cfSocket = NULL;
}
- // we take ownership of this CFFileDescriptorRef
- void SetFileDescriptor(CFFileDescriptorRef cffd);
+ // Finish initialization of the event loop source by providing the
+ // associated socket. This object takes ownership of it and will release it.
+ void InitSourceSocket(CFSocketRef cfSocket);
+ // Destructor deletes the associated socket.
virtual ~wxCFEventLoopSource();
private:
- CFFileDescriptorRef m_cffd;
+ CFSocketRef m_cfSocket;
wxDECLARE_NO_COPY_CLASS(wxCFEventLoopSource);
};
#include <sys/wait.h>
-#include <CoreFoundation/CFFileDescriptor.h>
#include <CoreFoundation/CFSocket.h>
/*!
namespace
{
-void EnableDescriptorCallBacks(CFFileDescriptorRef cffd, int flags)
-{
- if ( flags & wxEVENT_SOURCE_INPUT )
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
- if ( flags & wxEVENT_SOURCE_OUTPUT )
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack);
-}
-
+extern "C"
void
-wx_cffiledescriptor_callback(CFFileDescriptorRef cffd,
- CFOptionFlags flags,
- void *ctxData)
+wx_socket_callback(CFSocketRef WXUNUSED(s),
+ CFSocketCallBackType callbackType,
+ CFDataRef WXUNUSED(address),
+ void const *WXUNUSED(data),
+ void *ctxData)
{
wxLogTrace(wxTRACE_EVT_SOURCE,
- "CFFileDescriptor callback, flags=%d", flags);
+ "CFSocket callback, type=%d", callbackType);
wxCFEventLoopSource * const
source = static_cast<wxCFEventLoopSource *>(ctxData);
wxEventLoopSourceHandler * const
handler = source->GetHandler();
- if ( flags & kCFFileDescriptorReadCallBack )
- handler->OnReadWaiting();
- if ( flags & kCFFileDescriptorWriteCallBack )
- handler->OnWriteWaiting();
- // we need to re-enable callbacks to be called again
- EnableDescriptorCallBacks(cffd, source->GetFlags());
+ switch ( callbackType )
+ {
+ case kCFSocketReadCallBack:
+ handler->OnReadWaiting();
+ break;
+
+ case kCFSocketWriteCallBack:
+ handler->OnWriteWaiting();
+ break;
+
+ default:
+ wxFAIL_MSG( "Unexpected callback type." );
+ }
}
} // anonymous namespace
wxScopedPtr<wxCFEventLoopSource>
source(new wxCFEventLoopSource(handler, flags));
- CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL };
- wxCFRef<CFFileDescriptorRef>
- cffd(CFFileDescriptorCreate
- (
- kCFAllocatorDefault,
- fd,
- true, // close on invalidate
- wx_cffiledescriptor_callback,
- &ctx
- ));
- if ( !cffd )
+ CFSocketContext context = { 0, source.get(), NULL, NULL, NULL };
+
+ int callbackTypes = 0;
+ if ( flags & wxEVENT_SOURCE_INPUT )
+ callbackTypes |= kCFSocketReadCallBack;
+ if ( flags & wxEVENT_SOURCE_OUTPUT )
+ callbackTypes |= kCFSocketWriteCallBack;
+
+ wxCFRef<CFSocketRef>
+ cfSocket(CFSocketCreateWithNative
+ (
+ kCFAllocatorDefault,
+ fd,
+ callbackTypes,
+ &wx_socket_callback,
+ &context
+ ));
+
+ if ( !cfSocket )
+ {
+ wxLogError(wxS("Failed to create event loop source socket."));
return NULL;
+ }
+
+ // Adjust the socket options to suit our needs:
+ CFOptionFlags sockopt = CFSocketGetSocketFlags(cfSocket);
+
+ // First, by default, write callback is not called repeatedly when data
+ // can be written to the socket but we need this behaviour so request
+ // it explicitly.
+ if ( flags & wxEVENT_SOURCE_OUTPUT )
+ sockopt |= kCFSocketAutomaticallyReenableWriteCallBack;
+
+ // Second, we use the socket to monitor the FD but it doesn't own it,
+ // so prevent the FD from being closed when the socket is invalidated.
+ sockopt &= ~kCFSocketCloseOnInvalidate;
+
+ CFSocketSetSocketFlags(cfSocket, sockopt);
wxCFRef<CFRunLoopSourceRef>
- cfsrc(CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0));
- if ( !cfsrc )
+ runLoopSource(CFSocketCreateRunLoopSource
+ (
+ kCFAllocatorDefault,
+ cfSocket,
+ 0 // Lowest index means highest priority
+ ));
+ if ( !runLoopSource )
+ {
+ wxLogError(wxS("Failed to create low level event loop source."));
+ CFSocketInvalidate(cfSocket);
return NULL;
+ }
- CFRunLoopRef cfloop = CFRunLoopGetCurrent();
- CFRunLoopAddSource(cfloop, cfsrc, kCFRunLoopDefaultMode);
-
- // Enable the callbacks initially.
- EnableDescriptorCallBacks(cffd, source->GetFlags());
+ // Save the socket so that we can remove it later if asked to.
+ source->InitSourceSocket(cfSocket.release());
- source->SetFileDescriptor(cffd.release());
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
return source.release();
}