X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/aac04facffa150140571fce7de783d0c63d0e506..409e6ce4dcd39c6dfe8b77cbd56d451ffe5c731c:/src/mac/corefoundation/gsockosx.cpp diff --git a/src/mac/corefoundation/gsockosx.cpp b/src/mac/corefoundation/gsockosx.cpp index 8d888ec8d0..695dab74ca 100644 --- a/src/mac/corefoundation/gsockosx.cpp +++ b/src/mac/corefoundation/gsockosx.cpp @@ -1,181 +1,318 @@ /* ------------------------------------------------------------------------- * Project: GSocket (Generic Socket) for WX - * Name: gsockosx.c + * Name: src/mac/corefoundation/gsockosx.c * Purpose: GSocket: Mac OS X mach-o part * CVSID: $Id$ * Mac code by Brian Victor, February 2002. Email comments to bhv1@psu.edu * ------------------------------------------------------------------------- */ -#include "wx/setup.h" +#include "wx/wxprec.h" #if wxUSE_SOCKETS -#include #include "wx/gsocket.h" -#include "wx/unix/gsockunx.h" +#include "wx/apptrait.h" #include -#define ALL_CALLBACK_TYPES (kCFSocketReadCallBack | kCFSocketWriteCallBack | kCFSocketConnectCallBack) +// ---------------------------------------------------------------------------- +// Mac-specific data associated with each socket by GSocketCFManager +// ---------------------------------------------------------------------------- -struct MacGSocketData +class MacGSocketData { - CFSocketRef socket; - CFRunLoopSourceRef source; +public: + // default ctor creates the object in uninitialized state, use Initialize() + // later to make it usable + MacGSocketData() + { + m_socket = NULL; + m_source = NULL; + } + + // initialize the data associated with the given socket + bool Initialize(GSocket *socket) + { + wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" ); + + // we need a valid Unix socket to create a CFSocket + if ( socket->m_fd < 0 ) + return false; + + CFSocketContext cont; + cont.version = 0; // this currently must be 0 + cont.info = socket; // pointer passed to our callback + cont.retain = NULL; // no need to retain/release/copy the + cont.release = NULL; // socket pointer, so all callbacks + cont.copyDescription = NULL; // can be left NULL + + m_socket = CFSocketCreateWithNative + ( + NULL, // default allocator + socket->m_fd, + kCFSocketReadCallBack | + kCFSocketWriteCallBack | + kCFSocketConnectCallBack, + SocketCallback, + &cont + ); + if ( !m_socket ) + return false; + + m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0); + + return m_source != NULL; + } + + // free the objects created by Initialize() + ~MacGSocketData() + { + if ( m_source ) + CFRelease(m_source); + if ( m_socket ) + CFRelease(m_socket); + } + + // return true if Initialize() had already been called successfully + bool IsInitialized() const { return m_source && m_socket; } + + + // accessors: should only be called if IsInitialized() + CFSocketRef GetSocket() const + { + wxASSERT( IsInitialized() ); + + return m_socket; + } + + CFRunLoopSourceRef GetSource() const + { + wxASSERT( IsInitialized() ); + + return m_source; + } + +private: + static void SocketCallback(CFSocketRef WXUNUSED(s), + CFSocketCallBackType callbackType, + CFDataRef WXUNUSED(address), + const void* data, + void* info) + { + GSocket * const socket = wx_static_cast(GSocket *, info); + MacGSocketData * const + macdata = wx_static_cast(MacGSocketData *, socket->m_gui_dependent); + if ( !macdata ) + return; + + switch (callbackType) + { + case kCFSocketConnectCallBack: + wxASSERT(!socket->m_server); + // KH: If data is non-NULL, the connect failed, do not call Detected_Write, + // which will only end up creating a spurious connect event because the + // call to getsocketopt SO_ERROR inexplicably returns no error. + // The change in behavior cannot be traced to any particular commit or + // timeframe so I'm not sure what to think, but after so many hours, + // this seems to address the issue and it's time to move on. + if (data == NULL) + socket->Detected_Write(); + break; + + case kCFSocketReadCallBack: + socket->Detected_Read(); + break; + + case kCFSocketWriteCallBack: + socket->Detected_Write(); + break; + + default: + wxFAIL_MSG( "unexpected socket callback" ); + } + } + + CFSocketRef m_socket; + CFRunLoopSourceRef m_source; + + DECLARE_NO_COPY_CLASS(MacGSocketData); }; -void Mac_Socket_Callback(CFSocketRef s, CFSocketCallBackType callbackType, - CFDataRef address, const void* data, void* info) +// ---------------------------------------------------------------------------- +// CoreFoundation implementation of GSocketManager +// ---------------------------------------------------------------------------- + +class GSocketCFManager : public GSocketManager { - GSocket* socket = (GSocket*)info; - struct MacGSocketData* macdata; - macdata = (struct MacGSocketData*)socket->m_gui_dependent; - if (!macdata) return; - switch (callbackType) - { - case kCFSocketConnectCallBack: - assert(!socket->m_server); - socket->m_functions->Detected_Write(socket); - break; - case kCFSocketReadCallBack: - socket->m_functions->Detected_Read(socket); - break; - case kCFSocketWriteCallBack: - socket->m_functions->Detected_Write(socket); - break; - default: - break; /* We shouldn't get here. */ - } -} +public: + virtual bool OnInit(); + virtual void OnExit(); -struct MacGSocketData* _GSocket_Get_Mac_Socket(GSocket *socket) + virtual bool Init_Socket(GSocket *socket); + virtual void Destroy_Socket(GSocket *socket); + + virtual void Install_Callback(GSocket *socket, GSocketEvent event); + virtual void Uninstall_Callback(GSocket *socket, GSocketEvent event); + + virtual void Enable_Events(GSocket *socket); + virtual void Disable_Events(GSocket *socket); + +private: + // retrieve our custom data associated with the given socket + // + // this is a low level function, use GetInitializedData() instead if the + // data pointer should also be correctly initialized if it hadn't been done + // yet + // + // may return NULL if we hadn't created the data for this socket yet + MacGSocketData *GetData(GSocket *socket) const + { + return wx_static_cast(MacGSocketData *, socket->m_gui_dependent); + } + + // return the custom data pointer initializing it if it hadn't been done + // yet + // + // may return NULL if there is no associated data + MacGSocketData *GetInitializedData(GSocket *socket) const + { + MacGSocketData * const data = GetData(socket); + if ( data && !data->IsInitialized() ) + { + if ( !data->Initialize(socket) ) + return NULL; + } + + return data; + } + + // return CFSocket callback mask corresponding to the given event (the + // socket parameter is needed because some events are interpreted + // differently depending on whether they happen on a server or on a client + // socket) + static int GetCFCallback(GSocket *socket, GSocketEvent event); + + + // Sockets must use the event loop on the main thread so we store a + // reference to the main loop here in OnInit() + static CFRunLoopRef ms_mainRunLoop; +}; + +CFRunLoopRef GSocketCFManager::ms_mainRunLoop = NULL; + +bool GSocketCFManager::OnInit() { - /* If socket is already created, returns a pointer to the data */ - /* Otherwise, creates socket and returns the pointer */ - CFSocketContext cont; - struct MacGSocketData* data = (struct MacGSocketData*)socket->m_gui_dependent; - - if (data && data->source) return data; - - /* CFSocket has not been created, create it: */ - if (socket->m_fd < 0 || !data) return NULL; - cont.version = 0; cont.retain = NULL; - cont.release = NULL; cont.copyDescription = NULL; - cont.info = socket; - - CFSocketRef cf = CFSocketCreateWithNative(NULL, socket->m_fd, - ALL_CALLBACK_TYPES, Mac_Socket_Callback, &cont); - CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(NULL, cf, 0); - assert(source); - socket->m_gui_dependent = (char*)data; - - /* Keep the source and the socket around. */ - data->source = source; - data->socket = cf; - - return data; + // No need to store the main loop again + if (ms_mainRunLoop != NULL) + return true; + + // Get the loop for the main thread so our events will actually fire. + // The common socket.cpp code will assert if initialize is called from a + // secondary thread, otherwise Mac would have the same problems as MSW + ms_mainRunLoop = CFRunLoopGetCurrent(); + if ( !ms_mainRunLoop ) + return false; + + CFRetain(ms_mainRunLoop); + + return true; } -int _GSocket_GUI_Init(void) +void GSocketCFManager::OnExit() { - return 1; + // Release the reference count, and set the reference back to NULL + CFRelease(ms_mainRunLoop); + ms_mainRunLoop = NULL; } -void _GSocket_GUI_Cleanup(void) +bool GSocketCFManager::Init_Socket(GSocket *socket) { + socket->m_gui_dependent = new MacGSocketData; + return true; } -int _GSocket_GUI_Init_Socket(GSocket *socket) +void GSocketCFManager::Destroy_Socket(GSocket *socket) { - struct MacGSocketData *data = (struct MacGSocketData *) malloc(sizeof(struct MacGSocketData)); - if (data) + MacGSocketData * const data = GetData(socket); + if ( data ) { - socket->m_gui_dependent = (char*)data; - data->socket = NULL; - data->source = NULL; - return 1; + delete data; + socket->m_gui_dependent = NULL; } - return 0; } -void _GSocket_GUI_Destroy_Socket(GSocket *socket) +/* static */ +int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event) { - struct MacGSocketData *data = (struct MacGSocketData*)(socket->m_gui_dependent); - if (data) + switch ( event ) { - CFRelease(data->socket); - free(data); + case GSOCK_CONNECTION: + return socket->m_server ? kCFSocketReadCallBack + : kCFSocketConnectCallBack; + + case GSOCK_LOST: + case GSOCK_INPUT: + return kCFSocketReadCallBack; + + case GSOCK_OUTPUT: + return kCFSocketWriteCallBack; + + case GSOCK_MAX_EVENT: + wxFAIL_MSG( "invalid GSocketEvent" ); + return 0; + + default: + wxFAIL_MSG( "unknown GSocketEvent" ); + return 0; } } -void _GSocket_Install_Callback(GSocket *socket, GSocketEvent event) +void GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event) { - int c; - struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket); - if (!data) return; - switch (event) - { - case GSOCK_CONNECTION: - if(socket->m_server) - c = kCFSocketReadCallBack; - else - c = kCFSocketConnectCallBack; - break; - case GSOCK_LOST: - case GSOCK_INPUT: - c = kCFSocketReadCallBack; - break; - case GSOCK_OUTPUT: - c = kCFSocketWriteCallBack; - break; - default: - c = 0; - } - CFSocketEnableCallBacks(data->socket, c); + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event)); } -void _GSocket_Uninstall_Callback(GSocket *socket, GSocketEvent event) +void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event) { - int c; - struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket); - if (!data) return; - switch (event) - { - case GSOCK_CONNECTION: - if(socket->m_server) - c = kCFSocketReadCallBack; - else - c = kCFSocketConnectCallBack; - break; - case GSOCK_LOST: - case GSOCK_INPUT: - c = kCFSocketReadCallBack; - break; - case GSOCK_OUTPUT: - c = kCFSocketWriteCallBack; - break; - default: - c = 0; - } - CFSocketDisableCallBacks(data->socket, c); + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event)); } -void _GSocket_Enable_Events(GSocket *socket) +void GSocketCFManager::Enable_Events(GSocket *socket) { - struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket); - if (!data) return; + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; - CFRunLoopAddSource(CFRunLoopGetCurrent(), data->source, kCFRunLoopDefaultMode); + CFRunLoopAddSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes); } -void _GSocket_Disable_Events(GSocket *socket) +void GSocketCFManager::Disable_Events(GSocket *socket) { - struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket); - if (!data) return; + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + // CFSocketInvalidate does CFRunLoopRemoveSource anyway + CFRunLoopRemoveSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes); + CFSocketInvalidate(data->GetSocket()); - /* CFSocketInvalidate does CFRunLoopRemoveSource anyway */ - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), data->source, kCFRunLoopCommonModes); - CFSocketInvalidate(data->socket); + // CFSocketInvalidate has closed the socket so we want to make sure GSocket knows this + socket->m_fd = -1; } +GSocketManager *wxAppTraits::GetSocketManager() +{ + static GSocketCFManager s_manager; + + return &s_manager; +}; + #endif // wxUSE_SOCKETS