1 /* -------------------------------------------------------------------------
2 * Project: GSocket (Generic Socket) for WX
3 * Name: src/osx/corefoundation/gsockosx.c
4 * Purpose: GSocket: Mac OS X mach-o part
6 * Mac code by Brian Victor, February 2002. Email comments to bhv1@psu.edu
7 * ------------------------------------------------------------------------- */
13 #include "wx/gsocket.h"
14 #include "wx/apptrait.h"
16 #include <CoreFoundation/CoreFoundation.h>
21 // ----------------------------------------------------------------------------
23 // ----------------------------------------------------------------------------
25 // Sockets must use the event loop to monitor the events so we store a
26 // reference to the main thread event loop here
27 static CFRunLoopRef gs_mainRunLoop
= NULL
;
29 // ----------------------------------------------------------------------------
30 // Mac-specific data associated with each socket by GSocketCFManager
31 // ----------------------------------------------------------------------------
36 // default ctor creates the object in uninitialized state, use Initialize()
37 // later to make it usable
44 // initialize the data associated with the given socket
45 bool Initialize(GSocket
*socket
)
47 wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" );
49 // we need a valid Unix socket to create a CFSocket
50 if ( socket
->m_fd
< 0 )
54 cont
.version
= 0; // this currently must be 0
55 cont
.info
= socket
; // pointer passed to our callback
56 cont
.retain
= NULL
; // no need to retain/release/copy the
57 cont
.release
= NULL
; // socket pointer, so all callbacks
58 cont
.copyDescription
= NULL
; // can be left NULL
60 m_socket
= CFSocketCreateWithNative
62 NULL
, // default allocator
64 kCFSocketReadCallBack
|
65 kCFSocketWriteCallBack
|
66 kCFSocketConnectCallBack
,
73 m_source
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0);
81 CFRunLoopAddSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
86 // close the socket if it was opened
89 // VZ: CFRunLoopRemoveSource() is probably unnecessary as
90 // CFSocketInvalidate() seems to do it internally from reading the
91 // docs, please remove it (and this comment) after testing
92 CFRunLoopRemoveSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
);
93 CFSocketInvalidate(m_socket
);
101 wxASSERT_MSG( !m_source
&& !m_socket
, "forgot to call Close()?" );
104 // return true if Initialize() had already been called successfully
105 bool IsInitialized() const { return m_source
&& m_socket
; }
108 // accessors: should only be called if IsInitialized()
109 CFSocketRef
GetSocket() const
111 wxASSERT( IsInitialized() );
116 CFRunLoopSourceRef
GetSource() const
118 wxASSERT( IsInitialized() );
124 static void SocketCallback(CFSocketRef
WXUNUSED(s
),
125 CFSocketCallBackType callbackType
,
126 CFDataRef
WXUNUSED(address
),
130 GSocket
* const socket
= static_cast<GSocket
*>(info
);
131 MacGSocketData
* const
132 macdata
= static_cast<MacGSocketData
*>(socket
->m_gui_dependent
);
136 switch (callbackType
)
138 case kCFSocketConnectCallBack
:
139 wxASSERT(!socket
->m_server
);
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 behavior 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
->Detected_Write();
150 case kCFSocketReadCallBack
:
151 socket
->Detected_Read();
154 case kCFSocketWriteCallBack
:
155 socket
->Detected_Write();
159 wxFAIL_MSG( "unexpected socket callback" );
163 CFSocketRef m_socket
;
164 CFRunLoopSourceRef m_source
;
166 DECLARE_NO_COPY_CLASS(MacGSocketData
);
169 } // anonymous namespace
172 // ----------------------------------------------------------------------------
173 // CoreFoundation implementation of GSocketManager
174 // ----------------------------------------------------------------------------
176 class GSocketCFManager
: public GSocketManager
179 virtual bool OnInit();
180 virtual void OnExit();
182 virtual bool Init_Socket(GSocket
*socket
);
183 virtual void Close_Socket(GSocket
*socket
);
184 virtual void Destroy_Socket(GSocket
*socket
);
186 virtual void Install_Callback(GSocket
*socket
, GSocketEvent event
);
187 virtual void Uninstall_Callback(GSocket
*socket
, GSocketEvent event
);
190 // retrieve our custom data associated with the given socket
192 // this is a low level function, use GetInitializedData() instead if the
193 // data pointer should also be correctly initialized if it hadn't been done
196 // may return NULL if we hadn't created the data for this socket yet
197 MacGSocketData
*GetData(GSocket
*socket
) const
199 return static_cast<MacGSocketData
*>(socket
->m_gui_dependent
);
202 // return the custom data pointer initializing it if it hadn't been done
205 // may return NULL if there is no associated data
206 MacGSocketData
*GetInitializedData(GSocket
*socket
) const
208 MacGSocketData
* const data
= GetData(socket
);
209 if ( data
&& !data
->IsInitialized() )
211 if ( !data
->Initialize(socket
) )
218 // return CFSocket callback mask corresponding to the given event (the
219 // socket parameter is needed because some events are interpreted
220 // differently depending on whether they happen on a server or on a client
222 static int GetCFCallback(GSocket
*socket
, GSocketEvent event
);
225 bool GSocketCFManager::OnInit()
227 // No need to store the main loop again
228 if (gs_mainRunLoop
!= NULL
)
231 // Get the loop for the main thread so our events will actually fire.
232 // The common socket.cpp code will assert if initialize is called from a
233 // secondary thread, otherwise Mac would have the same problems as MSW
234 gs_mainRunLoop
= CFRunLoopGetCurrent();
235 if ( !gs_mainRunLoop
)
238 CFRetain(gs_mainRunLoop
);
243 void GSocketCFManager::OnExit()
245 // Release the reference count, and set the reference back to NULL
246 CFRelease(gs_mainRunLoop
);
247 gs_mainRunLoop
= NULL
;
250 bool GSocketCFManager::Init_Socket(GSocket
*socket
)
252 socket
->m_gui_dependent
= new MacGSocketData
;
256 void GSocketCFManager::Close_Socket(GSocket
*socket
)
258 Uninstall_Callback(socket
, GSOCK_INPUT
);
259 Uninstall_Callback(socket
, GSOCK_OUTPUT
);
261 MacGSocketData
* const data
= GetData(socket
);
266 void GSocketCFManager::Destroy_Socket(GSocket
*socket
)
268 MacGSocketData
* const data
= GetData(socket
);
272 socket
->m_gui_dependent
= NULL
;
277 int GSocketCFManager::GetCFCallback(GSocket
*socket
, GSocketEvent event
)
281 case GSOCK_CONNECTION
:
282 return socket
->m_server
? kCFSocketReadCallBack
283 : kCFSocketConnectCallBack
;
287 return kCFSocketReadCallBack
;
290 return kCFSocketWriteCallBack
;
292 case GSOCK_MAX_EVENT
:
293 wxFAIL_MSG( "invalid GSocketEvent" );
297 wxFAIL_MSG( "unknown GSocketEvent" );
302 void GSocketCFManager::Install_Callback(GSocket
*socket
, GSocketEvent event
)
304 const MacGSocketData
* const data
= GetInitializedData(socket
);
308 CFSocketEnableCallBacks(data
->GetSocket(), GetCFCallback(socket
, event
));
311 void GSocketCFManager::Uninstall_Callback(GSocket
*socket
, GSocketEvent event
)
313 const MacGSocketData
* const data
= GetInitializedData(socket
);
317 CFSocketDisableCallBacks(data
->GetSocket(), GetCFCallback(socket
, event
));
320 GSocketManager
*wxAppTraits::GetSocketManager()
322 static GSocketCFManager s_manager
;
327 #endif // wxUSE_SOCKETS