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>
18 // ----------------------------------------------------------------------------
19 // Mac-specific data associated with each socket by GSocketCFManager
20 // ----------------------------------------------------------------------------
25 // default ctor creates the object in uninitialized state, use Initialize()
26 // later to make it usable
33 // initialize the data associated with the given socket
34 bool Initialize(GSocket
*socket
)
36 wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" );
38 // we need a valid Unix socket to create a CFSocket
39 if ( socket
->m_fd
< 0 )
43 cont
.version
= 0; // this currently must be 0
44 cont
.info
= socket
; // pointer passed to our callback
45 cont
.retain
= NULL
; // no need to retain/release/copy the
46 cont
.release
= NULL
; // socket pointer, so all callbacks
47 cont
.copyDescription
= NULL
; // can be left NULL
49 m_socket
= CFSocketCreateWithNative
51 NULL
, // default allocator
53 kCFSocketReadCallBack
|
54 kCFSocketWriteCallBack
|
55 kCFSocketConnectCallBack
,
62 m_source
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0);
64 return m_source
!= NULL
;
67 // free the objects created by Initialize()
76 // return true if Initialize() had already been called successfully
77 bool IsInitialized() const { return m_source
&& m_socket
; }
80 // accessors: should only be called if IsInitialized()
81 CFSocketRef
GetSocket() const
83 wxASSERT( IsInitialized() );
88 CFRunLoopSourceRef
GetSource() const
90 wxASSERT( IsInitialized() );
96 static void SocketCallback(CFSocketRef
WXUNUSED(s
),
97 CFSocketCallBackType callbackType
,
98 CFDataRef
WXUNUSED(address
),
102 GSocket
* const socket
= wx_static_cast(GSocket
*, info
);
103 MacGSocketData
* const
104 macdata
= wx_static_cast(MacGSocketData
*, socket
->m_gui_dependent
);
108 switch (callbackType
)
110 case kCFSocketConnectCallBack
:
111 wxASSERT(!socket
->m_server
);
112 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
113 // which will only end up creating a spurious connect event because the
114 // call to getsocketopt SO_ERROR inexplicably returns no error.
115 // The change in behavior cannot be traced to any particular commit or
116 // timeframe so I'm not sure what to think, but after so many hours,
117 // this seems to address the issue and it's time to move on.
119 socket
->Detected_Write();
122 case kCFSocketReadCallBack
:
123 socket
->Detected_Read();
126 case kCFSocketWriteCallBack
:
127 socket
->Detected_Write();
131 wxFAIL_MSG( "unexpected socket callback" );
135 CFSocketRef m_socket
;
136 CFRunLoopSourceRef m_source
;
138 DECLARE_NO_COPY_CLASS(MacGSocketData
);
141 // ----------------------------------------------------------------------------
142 // CoreFoundation implementation of GSocketManager
143 // ----------------------------------------------------------------------------
145 class GSocketCFManager
: public GSocketManager
148 virtual bool OnInit();
149 virtual void OnExit();
151 virtual bool Init_Socket(GSocket
*socket
);
152 virtual void Destroy_Socket(GSocket
*socket
);
154 virtual void Install_Callback(GSocket
*socket
, GSocketEvent event
);
155 virtual void Uninstall_Callback(GSocket
*socket
, GSocketEvent event
);
157 virtual void Enable_Events(GSocket
*socket
);
158 virtual void Disable_Events(GSocket
*socket
);
161 // retrieve our custom data associated with the given socket
163 // this is a low level function, use GetInitializedData() instead if the
164 // data pointer should also be correctly initialized if it hadn't been done
167 // may return NULL if we hadn't created the data for this socket yet
168 MacGSocketData
*GetData(GSocket
*socket
) const
170 return wx_static_cast(MacGSocketData
*, socket
->m_gui_dependent
);
173 // return the custom data pointer initializing it if it hadn't been done
176 // may return NULL if there is no associated data
177 MacGSocketData
*GetInitializedData(GSocket
*socket
) const
179 MacGSocketData
* const data
= GetData(socket
);
180 if ( data
&& !data
->IsInitialized() )
182 if ( !data
->Initialize(socket
) )
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(GSocket
*socket
, GSocketEvent event
);
196 // Sockets must use the event loop on the main thread so we store a
197 // reference to the main loop here in OnInit()
198 static CFRunLoopRef ms_mainRunLoop
;
201 CFRunLoopRef
GSocketCFManager::ms_mainRunLoop
= NULL
;
203 bool GSocketCFManager::OnInit()
205 // No need to store the main loop again
206 if (ms_mainRunLoop
!= NULL
)
209 // Get the loop for the main thread so our events will actually fire.
210 // The common socket.cpp code will assert if initialize is called from a
211 // secondary thread, otherwise Mac would have the same problems as MSW
212 ms_mainRunLoop
= CFRunLoopGetCurrent();
213 if ( !ms_mainRunLoop
)
216 CFRetain(ms_mainRunLoop
);
221 void GSocketCFManager::OnExit()
223 // Release the reference count, and set the reference back to NULL
224 CFRelease(ms_mainRunLoop
);
225 ms_mainRunLoop
= NULL
;
228 bool GSocketCFManager::Init_Socket(GSocket
*socket
)
230 socket
->m_gui_dependent
= new MacGSocketData
;
234 void GSocketCFManager::Destroy_Socket(GSocket
*socket
)
236 MacGSocketData
* const data
= GetData(socket
);
240 socket
->m_gui_dependent
= NULL
;
245 int GSocketCFManager::GetCFCallback(GSocket
*socket
, GSocketEvent event
)
249 case GSOCK_CONNECTION
:
250 return socket
->m_server
? kCFSocketReadCallBack
251 : kCFSocketConnectCallBack
;
255 return kCFSocketReadCallBack
;
258 return kCFSocketWriteCallBack
;
260 case GSOCK_MAX_EVENT
:
261 wxFAIL_MSG( "invalid GSocketEvent" );
265 wxFAIL_MSG( "unknown GSocketEvent" );
270 void GSocketCFManager::Install_Callback(GSocket
*socket
, GSocketEvent event
)
272 const MacGSocketData
* const data
= GetInitializedData(socket
);
276 CFSocketEnableCallBacks(data
->GetSocket(), GetCFCallback(socket
, event
));
279 void GSocketCFManager::Uninstall_Callback(GSocket
*socket
, GSocketEvent event
)
281 const MacGSocketData
* const data
= GetInitializedData(socket
);
285 CFSocketDisableCallBacks(data
->GetSocket(), GetCFCallback(socket
, event
));
288 void GSocketCFManager::Enable_Events(GSocket
*socket
)
290 const MacGSocketData
* const data
= GetInitializedData(socket
);
294 CFRunLoopAddSource(ms_mainRunLoop
, data
->GetSource(), kCFRunLoopCommonModes
);
297 void GSocketCFManager::Disable_Events(GSocket
*socket
)
299 const MacGSocketData
* const data
= GetInitializedData(socket
);
303 // CFSocketInvalidate does CFRunLoopRemoveSource anyway
304 CFRunLoopRemoveSource(ms_mainRunLoop
, data
->GetSource(), kCFRunLoopCommonModes
);
305 CFSocketInvalidate(data
->GetSocket());
307 // CFSocketInvalidate has closed the socket so we want to make sure GSocket knows this
311 GSocketManager
*wxAppTraits::GetSocketManager()
313 static GSocketCFManager s_manager
;
318 #endif // wxUSE_SOCKETS