Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource.

Use OS X socket APIs for monitoring file descriptors. They are more flexible
than CFFileDescriptor functions and can be used with any descriptors, not
necessarily the socket ones.

See #10258.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74342 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2013-07-03 00:29:05 +00:00
parent 71e9885be0
commit a25b76f5f9
3 changed files with 90 additions and 48 deletions

View File

@ -11,7 +11,7 @@
#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
@ -20,19 +20,23 @@ typedef struct __CFFileDescriptor *CFFileDescriptorRef;
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);
};

View File

@ -42,23 +42,28 @@
#include "wx/nonownedwnd.h"
#endif
#include <CoreFoundation/CFSocket.h>
// ============================================================================
// wxCFEventLoopSource and wxCFEventLoop implementation
// ============================================================================
#if wxUSE_EVENTLOOP_SOURCE
void wxCFEventLoopSource::SetFileDescriptor(CFFileDescriptorRef cffd)
void wxCFEventLoopSource::InitSourceSocket(CFSocketRef cfSocket)
{
wxASSERT_MSG( !m_cffd, "shouldn't be called more than once" );
wxASSERT_MSG( !m_cfSocket, "shouldn't be called more than once" );
m_cffd = cffd;
m_cfSocket = cfSocket;
}
wxCFEventLoopSource::~wxCFEventLoopSource()
{
if ( m_cffd )
CFRelease(m_cffd);
if ( m_cfSocket )
{
CFSocketInvalidate(m_cfSocket);
CFRelease(m_cfSocket);
}
}
#endif // wxUSE_EVENTLOOP_SOURCE

View File

@ -28,7 +28,6 @@
#include <sys/wait.h>
#include <CoreFoundation/CFFileDescriptor.h>
#include <CoreFoundation/CFSocket.h>
/*!
@ -114,34 +113,36 @@ int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
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
@ -157,31 +158,63 @@ public:
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);
// Save the socket so that we can remove it later if asked to.
source->InitSourceSocket(cfSocket.release());
// Enable the callbacks initially.
EnableDescriptorCallBacks(cffd, source->GetFlags());
source->SetFileDescriptor(cffd.release());
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
return source.release();
}