]> git.saurik.com Git - wxWidgets.git/blob - src/osx/core/gsockosx.cpp
b3b7114e9fc0b3bb7232953f7cc179eb9dc1dbb4
[wxWidgets.git] / src / osx / core / gsockosx.cpp
1 /* -------------------------------------------------------------------------
2 * Project: GSocket (Generic Socket) for WX
3 * Name: src/osx/corefoundation/gsockosx.c
4 * Purpose: GSocket: Mac OS X mach-o part
5 * CVSID: $Id$
6 * Mac code by Brian Victor, February 2002. Email comments to bhv1@psu.edu
7 * ------------------------------------------------------------------------- */
8
9 #include "wx/wxprec.h"
10
11 #if wxUSE_SOCKETS
12
13 #include "wx/gsocket.h"
14 #include "wx/apptrait.h"
15
16 #include <CoreFoundation/CoreFoundation.h>
17
18 namespace
19 {
20
21 // ----------------------------------------------------------------------------
22 // global variables
23 // ----------------------------------------------------------------------------
24
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;
28
29 // ----------------------------------------------------------------------------
30 // Mac-specific data associated with each socket by GSocketCFManager
31 // ----------------------------------------------------------------------------
32
33 class MacGSocketData
34 {
35 public:
36 // default ctor creates the object in uninitialized state, use Initialize()
37 // later to make it usable
38 MacGSocketData()
39 {
40 m_socket = NULL;
41 m_source = NULL;
42 }
43
44 // initialize the data associated with the given socket
45 bool Initialize(GSocket *socket)
46 {
47 wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" );
48
49 // we need a valid Unix socket to create a CFSocket
50 if ( socket->m_fd < 0 )
51 return false;
52
53 CFSocketContext cont;
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
59
60 m_socket = CFSocketCreateWithNative
61 (
62 NULL, // default allocator
63 socket->m_fd,
64 kCFSocketReadCallBack |
65 kCFSocketWriteCallBack |
66 kCFSocketConnectCallBack,
67 SocketCallback,
68 &cont
69 );
70 if ( !m_socket )
71 return false;
72
73 m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
74
75 if ( !m_source )
76 {
77 CFRelease(m_socket);
78 return false;
79 }
80
81 CFRunLoopAddSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
82
83 return true;
84 }
85
86 // close the socket if it was opened
87 void Close()
88 {
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);
94
95 CFRelease(m_source);
96 CFRelease(m_socket);
97 }
98
99 ~MacGSocketData()
100 {
101 wxASSERT_MSG( !m_source && !m_socket, "forgot to call Close()?" );
102 }
103
104 // return true if Initialize() had already been called successfully
105 bool IsInitialized() const { return m_source && m_socket; }
106
107
108 // accessors: should only be called if IsInitialized()
109 CFSocketRef GetSocket() const
110 {
111 wxASSERT( IsInitialized() );
112
113 return m_socket;
114 }
115
116 CFRunLoopSourceRef GetSource() const
117 {
118 wxASSERT( IsInitialized() );
119
120 return m_source;
121 }
122
123 private:
124 static void SocketCallback(CFSocketRef WXUNUSED(s),
125 CFSocketCallBackType callbackType,
126 CFDataRef WXUNUSED(address),
127 const void* data,
128 void* info)
129 {
130 GSocket * const socket = static_cast<GSocket *>(info);
131 MacGSocketData * const
132 macdata = static_cast<MacGSocketData *>(socket->m_gui_dependent);
133 if ( !macdata )
134 return;
135
136 switch (callbackType)
137 {
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.
146 if (data == NULL)
147 socket->Detected_Write();
148 break;
149
150 case kCFSocketReadCallBack:
151 socket->Detected_Read();
152 break;
153
154 case kCFSocketWriteCallBack:
155 socket->Detected_Write();
156 break;
157
158 default:
159 wxFAIL_MSG( "unexpected socket callback" );
160 }
161 }
162
163 CFSocketRef m_socket;
164 CFRunLoopSourceRef m_source;
165
166 DECLARE_NO_COPY_CLASS(MacGSocketData);
167 };
168
169 } // anonymous namespace
170
171
172 // ----------------------------------------------------------------------------
173 // CoreFoundation implementation of GSocketManager
174 // ----------------------------------------------------------------------------
175
176 class GSocketCFManager : public GSocketManager
177 {
178 public:
179 virtual bool OnInit();
180 virtual void OnExit();
181
182 virtual bool Init_Socket(GSocket *socket);
183 virtual void Close_Socket(GSocket *socket);
184 virtual void Destroy_Socket(GSocket *socket);
185
186 virtual void Install_Callback(GSocket *socket, GSocketEvent event);
187 virtual void Uninstall_Callback(GSocket *socket, GSocketEvent event);
188
189 private:
190 // retrieve our custom data associated with the given socket
191 //
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
194 // yet
195 //
196 // may return NULL if we hadn't created the data for this socket yet
197 MacGSocketData *GetData(GSocket *socket) const
198 {
199 return static_cast<MacGSocketData *>(socket->m_gui_dependent);
200 }
201
202 // return the custom data pointer initializing it if it hadn't been done
203 // yet
204 //
205 // may return NULL if there is no associated data
206 MacGSocketData *GetInitializedData(GSocket *socket) const
207 {
208 MacGSocketData * const data = GetData(socket);
209 if ( data && !data->IsInitialized() )
210 {
211 if ( !data->Initialize(socket) )
212 return NULL;
213 }
214
215 return data;
216 }
217
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
221 // socket)
222 static int GetCFCallback(GSocket *socket, GSocketEvent event);
223 };
224
225 bool GSocketCFManager::OnInit()
226 {
227 // No need to store the main loop again
228 if (gs_mainRunLoop != NULL)
229 return true;
230
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 )
236 return false;
237
238 CFRetain(gs_mainRunLoop);
239
240 return true;
241 }
242
243 void GSocketCFManager::OnExit()
244 {
245 // Release the reference count, and set the reference back to NULL
246 CFRelease(gs_mainRunLoop);
247 gs_mainRunLoop = NULL;
248 }
249
250 bool GSocketCFManager::Init_Socket(GSocket *socket)
251 {
252 socket->m_gui_dependent = new MacGSocketData;
253 return true;
254 }
255
256 void GSocketCFManager::Close_Socket(GSocket *socket)
257 {
258 Uninstall_Callback(socket, GSOCK_INPUT);
259 Uninstall_Callback(socket, GSOCK_OUTPUT);
260
261 MacGSocketData * const data = GetData(socket);
262 if ( data )
263 data->Close();
264 }
265
266 void GSocketCFManager::Destroy_Socket(GSocket *socket)
267 {
268 MacGSocketData * const data = GetData(socket);
269 if ( data )
270 {
271 delete data;
272 socket->m_gui_dependent = NULL;
273 }
274 }
275
276 /* static */
277 int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event)
278 {
279 switch ( event )
280 {
281 case GSOCK_CONNECTION:
282 return socket->m_server ? kCFSocketReadCallBack
283 : kCFSocketConnectCallBack;
284
285 case GSOCK_LOST:
286 case GSOCK_INPUT:
287 return kCFSocketReadCallBack;
288
289 case GSOCK_OUTPUT:
290 return kCFSocketWriteCallBack;
291
292 case GSOCK_MAX_EVENT:
293 wxFAIL_MSG( "invalid GSocketEvent" );
294 return 0;
295
296 default:
297 wxFAIL_MSG( "unknown GSocketEvent" );
298 return 0;
299 }
300 }
301
302 void GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event)
303 {
304 const MacGSocketData * const data = GetInitializedData(socket);
305 if ( !data )
306 return;
307
308 CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
309 }
310
311 void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event)
312 {
313 const MacGSocketData * const data = GetInitializedData(socket);
314 if ( !data )
315 return;
316
317 CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
318 }
319
320 GSocketManager *wxAppTraits::GetSocketManager()
321 {
322 static GSocketCFManager s_manager;
323
324 return &s_manager;
325 };
326
327 #endif // wxUSE_SOCKETS