]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/corefoundation/gsockosx.cpp
another fix for HP-UX 11.11 vsscanf, should have been in r51260
[wxWidgets.git] / src / mac / corefoundation / gsockosx.cpp
index 52e93233471631ee464d3c4a7baa4d85a2f30702..695dab74ca3aefcf0c74325e4e5df4ffb44773b3 100644 (file)
 /* -------------------------------------------------------------------------
  * 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 <stdlib.h>
 #include "wx/gsocket.h"
-#include "wx/unix/gsockunx.h"
+#include "wx/apptrait.h"
 
 #include <CoreFoundation/CoreFoundation.h>
 
-#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);
 };
 
-// Sockets must use the event loop on the main thread
-// We will store the main loop's reference when Initialize is called
-static CFRunLoopRef s_mainRunLoop = NULL;
+// ----------------------------------------------------------------------------
+// CoreFoundation implementation of GSocketManager
+// ----------------------------------------------------------------------------
 
-void Mac_Socket_Callback(CFSocketRef s, CFSocketCallBackType callbackType,
-                         CFDataRef address, const void* data, void* info)
+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->Detected_Write();
-      break;
-    case kCFSocketReadCallBack:
-      socket->Detected_Read();
-      break;
-    case kCFSocketWriteCallBack:
-      socket->Detected_Write();
-      break;
-    default:
-      break;  /* We shouldn't get here. */
-  }
-}
+public:
+    virtual bool OnInit();
+    virtual void OnExit();
 
-struct MacGSocketData* _GSocket_Get_Mac_Socket(GSocket *socket)
-{
-  /* 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;
-}
+    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);
 
-bool GSocketGUIFunctionsTableConcrete::CanUseEventLoop()
-{   return true; }
+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;
+    }
 
-bool GSocketGUIFunctionsTableConcrete::OnInit(void)
+    // 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()
 {
     // No need to store the main loop again
-    if (s_mainRunLoop != NULL)
+    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
-    s_mainRunLoop = CFRunLoopGetCurrent();
-    CFRetain(s_mainRunLoop);
+    ms_mainRunLoop = CFRunLoopGetCurrent();
+    if ( !ms_mainRunLoop )
+        return false;
+
+    CFRetain(ms_mainRunLoop);
 
     return true;
 }
 
-void GSocketGUIFunctionsTableConcrete::OnExit(void)
+void GSocketCFManager::OnExit()
 {
     // Release the reference count, and set the reference back to NULL
-    CFRelease(s_mainRunLoop);
-    s_mainRunLoop = NULL;
+    CFRelease(ms_mainRunLoop);
+    ms_mainRunLoop = NULL;
 }
 
-bool GSocketGUIFunctionsTableConcrete::Init_Socket(GSocket *socket)
+bool GSocketCFManager::Init_Socket(GSocket *socket)
 {
-    struct MacGSocketData *data = (struct MacGSocketData *)malloc(sizeof(struct MacGSocketData));
-    if (data)
-    {
-        socket->m_gui_dependent = (char*)data;
-        data->socket = NULL;
-        data->source = NULL;
-        return 1;
-    }
-    return 0;
+    socket->m_gui_dependent = new MacGSocketData;
+    return true;
 }
 
-void GSocketGUIFunctionsTableConcrete::Destroy_Socket(GSocket *socket)
+void GSocketCFManager::Destroy_Socket(GSocket *socket)
 {
-    struct MacGSocketData *data = (struct MacGSocketData*)(socket->m_gui_dependent);
-    if (data)
+    MacGSocketData * const data = GetData(socket);
+    if ( data )
     {
-        CFRelease(data->socket);
-        free(data);
+        delete data;
+        socket->m_gui_dependent = NULL;
     }
 }
 
-void GSocketGUIFunctionsTableConcrete::Install_Callback(GSocket *socket, GSocketEvent event)
+/* static */
+int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event)
 {
-    int c;
-    struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket);
-    if (!data) return;
-    switch (event)
+    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;
+        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;
     }
-    CFSocketEnableCallBacks(data->socket, c);
 }
 
-void GSocketGUIFunctionsTableConcrete::Uninstall_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;
-    }
-    CFSocketDisableCallBacks(data->socket, c);
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
+
+    CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
 }
 
-void GSocketGUIFunctionsTableConcrete::Enable_Events(GSocket *socket)
+void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event)
 {
-    struct MacGSocketData* data = _GSocket_Get_Mac_Socket(socket);
-    if (!data) return;
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
 
-    CFRunLoopAddSource(s_mainRunLoop, data->source, kCFRunLoopCommonModes);
+    CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
 }
 
-void GSocketGUIFunctionsTableConcrete::Disable_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;
 
-    /* CFSocketInvalidate does CFRunLoopRemoveSource anyway */
-    CFRunLoopRemoveSource(s_mainRunLoop, data->source, kCFRunLoopCommonModes);
-    CFSocketInvalidate(data->socket);
+    CFRunLoopAddSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes);
 }
 
+void GSocketCFManager::Disable_Events(GSocket *socket)
+{
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
+
+    // CFSocketInvalidate does CFRunLoopRemoveSource anyway
+    CFRunLoopRemoveSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes);
+    CFSocketInvalidate(data->GetSocket());
+
+    // 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