1 /////////////////////////////////////////////////////////////////////////////
2 // Name: osx/core/gsockosx.cpp
3 // Purpose: wxSocketImpl implementation for OS X
4 // Authors: Brian Victor, Vadim Zeitlin
5 // Created: February 2002
7 // Copyright: (c) 2002 Brian Victor
8 // (c) 2008 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
16 #include "wx/private/socket.h"
17 #include "wx/unix/private/sockunix.h"
18 #include "wx/apptrait.h"
20 #include <CoreFoundation/CoreFoundation.h>
25 // ----------------------------------------------------------------------------
27 // ----------------------------------------------------------------------------
29 // Sockets must use the event loop to monitor the events so we store a
30 // reference to the main thread event loop here
31 static CFRunLoopRef gs_mainRunLoop
= NULL
;
33 // ----------------------------------------------------------------------------
34 // Mac-specific socket implementation
35 // ----------------------------------------------------------------------------
37 class wxSocketImplMac
: public wxSocketImplUnix
40 wxSocketImplMac(wxSocketBase
& wxsocket
)
41 : wxSocketImplUnix(wxsocket
)
47 virtual ~wxSocketImplMac()
49 wxASSERT_MSG( !m_source
&& !m_socket
, "forgot to call Close()?" );
52 // get the underlying socket: creates it on demand
53 CFSocketRef
GetSocket() /* const */
62 virtual void DoClose()
64 wxSocketManager
* const manager
= wxSocketManager::Get();
67 manager
->Uninstall_Callback(this, wxSOCKET_INPUT
);
68 manager
->Uninstall_Callback(this, wxSOCKET_OUTPUT
);
71 // VZ: CFRunLoopRemoveSource() is probably unnecessary as
72 // CFSocketInvalidate() seems to do it internally from reading the
73 // docs, please remove it (and this comment) after testing
74 CFRunLoopRemoveSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
75 CFSocketInvalidate(m_socket
);
81 // initialize the data associated with the given socket
84 // we need a valid Unix socket to create a CFSocket
89 cont
.version
= 0; // this currently must be 0
90 cont
.info
= this; // pointer passed to our callback
91 cont
.retain
= NULL
; // no need to retain/release/copy the
92 cont
.release
= NULL
; // socket pointer, so all callbacks
93 cont
.copyDescription
= NULL
; // can be left NULL
95 m_socket
= CFSocketCreateWithNative
97 NULL
, // default allocator
99 kCFSocketReadCallBack
|
100 kCFSocketWriteCallBack
|
101 kCFSocketConnectCallBack
,
108 m_source
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0);
116 CFRunLoopAddSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
121 static void SocketCallback(CFSocketRef
WXUNUSED(s
),
122 CFSocketCallBackType callbackType
,
123 CFDataRef
WXUNUSED(address
),
127 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(info
);
129 switch (callbackType
)
131 case kCFSocketConnectCallBack
:
132 wxASSERT(!socket
->IsServer());
133 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
134 // which will only end up creating a spurious connect event because the
135 // call to getsocketopt SO_ERROR inexplicably returns no error.
136 // The change in behavior cannot be traced to any particular commit or
137 // timeframe so I'm not sure what to think, but after so many hours,
138 // this seems to address the issue and it's time to move on.
140 socket
->OnWriteWaiting();
143 case kCFSocketReadCallBack
:
144 socket
->OnReadWaiting();
147 case kCFSocketWriteCallBack
:
148 socket
->OnWriteWaiting();
152 wxFAIL_MSG( "unexpected socket callback" );
156 CFSocketRef m_socket
;
157 CFRunLoopSourceRef m_source
;
159 wxDECLARE_NO_COPY_CLASS(wxSocketImplMac
);
162 } // anonymous namespace
165 // ----------------------------------------------------------------------------
166 // CoreFoundation implementation of wxSocketManager
167 // ----------------------------------------------------------------------------
169 class wxSocketManagerMac
: public wxSocketManager
172 virtual bool OnInit();
173 virtual void OnExit();
175 virtual wxSocketImpl
*CreateSocket(wxSocketBase
& wxsocket
)
177 return new wxSocketImplMac(wxsocket
);
180 virtual void Install_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
181 virtual void Uninstall_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
184 // return CFSocket callback mask corresponding to the given event (the
185 // socket parameter is needed because some events are interpreted
186 // differently depending on whether they happen on a server or on a client
188 static int GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
);
191 bool wxSocketManagerMac::OnInit()
193 // No need to store the main loop again
194 if (gs_mainRunLoop
!= NULL
)
197 // Get the loop for the main thread so our events will actually fire.
198 // The common socket.cpp code will assert if initialize is called from a
199 // secondary thread, otherwise Mac would have the same problems as MSW
200 gs_mainRunLoop
= CFRunLoopGetCurrent();
201 if ( !gs_mainRunLoop
)
204 CFRetain(gs_mainRunLoop
);
209 void wxSocketManagerMac::OnExit()
211 // Release the reference count, and set the reference back to NULL
212 CFRelease(gs_mainRunLoop
);
213 gs_mainRunLoop
= NULL
;
217 int wxSocketManagerMac::GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
)
221 case wxSOCKET_CONNECTION
:
222 return socket
->IsServer() ? kCFSocketReadCallBack
223 : kCFSocketConnectCallBack
;
226 return kCFSocketReadCallBack
;
228 case wxSOCKET_OUTPUT
:
229 return kCFSocketWriteCallBack
;
232 wxFAIL_MSG( "unexpected wxSocketNotify" );
236 wxFAIL_MSG( "unknown wxSocketNotify" );
241 void wxSocketManagerMac::Install_Callback(wxSocketImpl
*socket_
,
242 wxSocketNotify event
)
244 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
246 CFSocketEnableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
249 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl
*socket_
,
250 wxSocketNotify event
)
252 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
254 CFSocketDisableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
257 // set the wxBase variable to point to our wxSocketManager implementation
259 // see comments in wx/apptrait.h for the explanation of why do we do it
261 static struct ManagerSetter
265 static wxSocketManagerMac s_manager
;
266 wxAppTraits::SetDefaultSocketManager(&s_manager
);
270 #endif // wxUSE_SOCKETS