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
);
84 // initialize the data associated with the given socket
87 // we need a valid Unix socket to create a CFSocket
92 cont
.version
= 0; // this currently must be 0
93 cont
.info
= this; // pointer passed to our callback
94 cont
.retain
= NULL
; // no need to retain/release/copy the
95 cont
.release
= NULL
; // socket pointer, so all callbacks
96 cont
.copyDescription
= NULL
; // can be left NULL
98 m_socket
= CFSocketCreateWithNative
100 NULL
, // default allocator
102 kCFSocketReadCallBack
|
103 kCFSocketWriteCallBack
|
104 kCFSocketConnectCallBack
,
111 m_source
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0);
121 CFRunLoopAddSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
126 static void SocketCallback(CFSocketRef
WXUNUSED(s
),
127 CFSocketCallBackType callbackType
,
128 CFDataRef
WXUNUSED(address
),
132 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(info
);
134 switch (callbackType
)
136 case kCFSocketConnectCallBack
:
137 wxASSERT(!socket
->IsServer());
138 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
139 // which will only end up creating a spurious connect event because the
140 // call to getsocketopt SO_ERROR inexplicably returns no error.
141 // The change in behavior cannot be traced to any particular commit or
142 // timeframe so I'm not sure what to think, but after so many hours,
143 // this seems to address the issue and it's time to move on.
145 socket
->OnWriteWaiting();
148 case kCFSocketReadCallBack
:
149 socket
->OnReadWaiting();
152 case kCFSocketWriteCallBack
:
153 socket
->OnWriteWaiting();
157 wxFAIL_MSG( "unexpected socket callback" );
161 CFSocketRef m_socket
;
162 CFRunLoopSourceRef m_source
;
164 wxDECLARE_NO_COPY_CLASS(wxSocketImplMac
);
167 } // anonymous namespace
170 // ----------------------------------------------------------------------------
171 // CoreFoundation implementation of wxSocketManager
172 // ----------------------------------------------------------------------------
174 class wxSocketManagerMac
: public wxSocketManager
177 virtual bool OnInit();
178 virtual void OnExit();
180 virtual wxSocketImpl
*CreateSocket(wxSocketBase
& wxsocket
)
182 return new wxSocketImplMac(wxsocket
);
185 virtual void Install_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
186 virtual void Uninstall_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
189 // return CFSocket callback mask corresponding to the given event (the
190 // socket parameter is needed because some events are interpreted
191 // differently depending on whether they happen on a server or on a client
193 static int GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
);
196 bool wxSocketManagerMac::OnInit()
198 // No need to store the main loop again
199 if (gs_mainRunLoop
!= NULL
)
202 // Get the loop for the main thread so our events will actually fire.
203 // The common socket.cpp code will assert if initialize is called from a
204 // secondary thread, otherwise Mac would have the same problems as MSW
205 gs_mainRunLoop
= CFRunLoopGetCurrent();
206 if ( !gs_mainRunLoop
)
209 CFRetain(gs_mainRunLoop
);
214 void wxSocketManagerMac::OnExit()
216 // Release the reference count, and set the reference back to NULL
217 CFRelease(gs_mainRunLoop
);
218 gs_mainRunLoop
= NULL
;
222 int wxSocketManagerMac::GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
)
226 case wxSOCKET_CONNECTION
:
227 return socket
->IsServer() ? kCFSocketReadCallBack
228 : kCFSocketConnectCallBack
;
231 return kCFSocketReadCallBack
;
233 case wxSOCKET_OUTPUT
:
234 return kCFSocketWriteCallBack
;
237 wxFAIL_MSG( "unexpected wxSocketNotify" );
241 wxFAIL_MSG( "unknown wxSocketNotify" );
246 void wxSocketManagerMac::Install_Callback(wxSocketImpl
*socket_
,
247 wxSocketNotify event
)
249 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
251 CFSocketEnableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
254 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl
*socket_
,
255 wxSocketNotify event
)
257 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
259 CFSocketDisableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
262 // set the wxBase variable to point to CF wxSocketManager implementation so
263 // that the GUI code in utilsexc_cf.cpp could return it from its traits method
265 // this is very roundabout but necessary to allow us to have different
266 // behaviours in console and GUI applications while avoiding dependencies of
267 // GUI library on the network one
268 extern WXDLLIMPEXP_BASE wxSocketManager
*wxOSXSocketManagerCF
;
270 static struct OSXManagerSetter
274 static wxSocketManagerMac s_manager
;
275 wxOSXSocketManagerCF
= &s_manager
;
277 } gs_OSXManagerSetter
;
279 #endif // wxUSE_SOCKETS