]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/core/gsockosx.cpp
renaming
[wxWidgets.git] / src / mac / core / gsockosx.cpp
diff --git a/src/mac/core/gsockosx.cpp b/src/mac/core/gsockosx.cpp
new file mode 100644 (file)
index 0000000..695dab7
--- /dev/null
@@ -0,0 +1,318 @@
+/* -------------------------------------------------------------------------
+ * Project: GSocket (Generic Socket) for WX
+ * 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/wxprec.h"
+
+#if wxUSE_SOCKETS
+
+#include "wx/gsocket.h"
+#include "wx/apptrait.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+// ----------------------------------------------------------------------------
+// Mac-specific data associated with each socket by GSocketCFManager
+// ----------------------------------------------------------------------------
+
+class MacGSocketData
+{
+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);
+};
+
+// ----------------------------------------------------------------------------
+// CoreFoundation implementation of GSocketManager
+// ----------------------------------------------------------------------------
+
+class GSocketCFManager : public GSocketManager
+{
+public:
+    virtual bool OnInit();
+    virtual void OnExit();
+
+    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()
+{
+    // 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;
+}
+
+void GSocketCFManager::OnExit()
+{
+    // Release the reference count, and set the reference back to NULL
+    CFRelease(ms_mainRunLoop);
+    ms_mainRunLoop = NULL;
+}
+
+bool GSocketCFManager::Init_Socket(GSocket *socket)
+{
+    socket->m_gui_dependent = new MacGSocketData;
+    return true;
+}
+
+void GSocketCFManager::Destroy_Socket(GSocket *socket)
+{
+    MacGSocketData * const data = GetData(socket);
+    if ( data )
+    {
+        delete data;
+        socket->m_gui_dependent = NULL;
+    }
+}
+
+/* static */
+int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event)
+{
+    switch ( event )
+    {
+        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 GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event)
+{
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
+
+    CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
+}
+
+void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event)
+{
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
+
+    CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
+}
+
+void GSocketCFManager::Enable_Events(GSocket *socket)
+{
+    const MacGSocketData * const data = GetInitializedData(socket);
+    if ( !data )
+        return;
+
+    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