1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/core/gsockosx.cpp
3 // Purpose: wxSocketImpl implementation for OS X
4 // Authors: Brian Victor, Vadim Zeitlin
5 // Created: February 2002
6 // Copyright: (c) 2002 Brian Victor
7 // (c) 2008 Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
15 #include "wx/private/socket.h"
16 #include "wx/unix/private/sockunix.h"
17 #include "wx/apptrait.h"
20 #include "wx/osx/core/cfstring.h" // for wxMacWakeUp() only
22 #include <CoreFoundation/CoreFoundation.h>
27 // ----------------------------------------------------------------------------
29 // ----------------------------------------------------------------------------
31 // Sockets must use the event loop to monitor the events so we store a
32 // reference to the main thread event loop here
33 static CFRunLoopRef gs_mainRunLoop
= NULL
;
35 // ----------------------------------------------------------------------------
36 // Mac-specific socket implementation
37 // ----------------------------------------------------------------------------
39 class wxSocketImplMac
: public wxSocketImplUnix
42 wxSocketImplMac(wxSocketBase
& wxsocket
)
43 : wxSocketImplUnix(wxsocket
)
49 virtual ~wxSocketImplMac()
51 wxASSERT_MSG( !m_source
&& !m_socket
, "forgot to call Close()?" );
54 // get the underlying socket: creates it on demand
55 CFSocketRef
GetSocket() /* const */
64 virtual void DoClose()
66 wxSocketManager
* const manager
= wxSocketManager::Get();
69 manager
->Uninstall_Callback(this, wxSOCKET_INPUT
);
70 manager
->Uninstall_Callback(this, wxSOCKET_OUTPUT
);
73 // VZ: CFRunLoopRemoveSource() is probably unnecessary as
74 // CFSocketInvalidate() seems to do it internally from reading the
75 // docs, please remove it (and this comment) after testing
76 CFRunLoopRemoveSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
77 CFSocketInvalidate(m_socket
);
86 // initialize the data associated with the given socket
89 // we need a valid Unix socket to create a CFSocket
94 cont
.version
= 0; // this currently must be 0
95 cont
.info
= this; // pointer passed to our callback
96 cont
.retain
= NULL
; // no need to retain/release/copy the
97 cont
.release
= NULL
; // socket pointer, so all callbacks
98 cont
.copyDescription
= NULL
; // can be left NULL
100 m_socket
= CFSocketCreateWithNative
102 NULL
, // default allocator
104 kCFSocketReadCallBack
|
105 kCFSocketWriteCallBack
|
106 kCFSocketConnectCallBack
,
113 m_source
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0);
123 CFRunLoopAddSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
128 static void SocketCallback(CFSocketRef
WXUNUSED(s
),
129 CFSocketCallBackType callbackType
,
130 CFDataRef
WXUNUSED(address
),
134 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(info
);
136 switch (callbackType
)
138 case kCFSocketConnectCallBack
:
139 wxASSERT(!socket
->IsServer());
140 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
141 // which will only end up creating a spurious connect event because the
142 // call to getsocketopt SO_ERROR inexplicably returns no error.
143 // The change in behaviour cannot be traced to any particular commit or
144 // timeframe so I'm not sure what to think, but after so many hours,
145 // this seems to address the issue and it's time to move on.
147 socket
->OnWriteWaiting();
150 case kCFSocketReadCallBack
:
151 socket
->OnReadWaiting();
154 case kCFSocketWriteCallBack
:
155 socket
->OnWriteWaiting();
159 wxFAIL_MSG( "unexpected socket callback" );
162 // receiving a socket event does _not_ make ReceiveNextEvent() (or the
163 // equivalent NSApp:nextEventMatchingMask:untilDate:inMode:dequeue)
164 // return control, i.e. apparently it doesn't count as a real event, so
165 // we need to generate a wake up to return control to the code waiting
166 // for something to happen and process this socket event
170 CFSocketRef m_socket
;
171 CFRunLoopSourceRef m_source
;
173 wxDECLARE_NO_COPY_CLASS(wxSocketImplMac
);
176 } // anonymous namespace
179 // ----------------------------------------------------------------------------
180 // CoreFoundation implementation of wxSocketManager
181 // ----------------------------------------------------------------------------
183 class wxSocketManagerMac
: public wxSocketManager
186 virtual bool OnInit();
187 virtual void OnExit();
189 virtual wxSocketImpl
*CreateSocket(wxSocketBase
& wxsocket
)
191 return new wxSocketImplMac(wxsocket
);
194 virtual void Install_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
195 virtual void Uninstall_Callback(wxSocketImpl
*socket
, wxSocketNotify event
);
198 // return CFSocket callback mask corresponding to the given event (the
199 // socket parameter is needed because some events are interpreted
200 // differently depending on whether they happen on a server or on a client
202 static int GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
);
205 bool wxSocketManagerMac::OnInit()
207 // No need to store the main loop again
208 if (gs_mainRunLoop
!= NULL
)
211 // Get the loop for the main thread so our events will actually fire.
212 // The common socket.cpp code will assert if initialize is called from a
213 // secondary thread, otherwise Mac would have the same problems as MSW
214 gs_mainRunLoop
= CFRunLoopGetCurrent();
215 if ( !gs_mainRunLoop
)
218 CFRetain(gs_mainRunLoop
);
223 void wxSocketManagerMac::OnExit()
225 // Release the reference count, and set the reference back to NULL
226 CFRelease(gs_mainRunLoop
);
227 gs_mainRunLoop
= NULL
;
231 int wxSocketManagerMac::GetCFCallback(wxSocketImpl
*socket
, wxSocketNotify event
)
235 case wxSOCKET_CONNECTION
:
236 return socket
->IsServer() ? kCFSocketReadCallBack
237 : kCFSocketConnectCallBack
;
240 return kCFSocketReadCallBack
;
242 case wxSOCKET_OUTPUT
:
243 return kCFSocketWriteCallBack
;
246 wxFAIL_MSG( "unexpected wxSocketNotify" );
250 wxFAIL_MSG( "unknown wxSocketNotify" );
255 void wxSocketManagerMac::Install_Callback(wxSocketImpl
*socket_
,
256 wxSocketNotify event
)
258 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
260 CFSocketEnableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
263 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl
*socket_
,
264 wxSocketNotify event
)
266 wxSocketImplMac
* const socket
= static_cast<wxSocketImplMac
*>(socket_
);
268 CFSocketDisableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
));
271 // set the wxBase variable to point to CF wxSocketManager implementation so
272 // that the GUI code in utilsexc_cf.cpp could return it from its traits method
274 // this is very roundabout but necessary to allow us to have different
275 // behaviours in console and GUI applications while avoiding dependencies of
276 // GUI library on the network one
277 extern WXDLLIMPEXP_BASE wxSocketManager
*wxOSXSocketManagerCF
;
279 static struct OSXManagerSetter
283 static wxSocketManagerMac s_manager
;
284 wxOSXSocketManagerCF
= &s_manager
;
286 } gs_OSXManagerSetter
;
288 // see the relative linker macro in socket.cpp
289 wxFORCE_LINK_THIS_MODULE(osxsocket
)
291 #endif // wxUSE_SOCKETS