]> git.saurik.com Git - wxWidgets.git/blob - src/mac/corefoundation/gsockosx.cpp
include 'Version' in version string, it's what Apple apps do
[wxWidgets.git] / src / mac / corefoundation / gsockosx.cpp
1 /* -------------------------------------------------------------------------
2 * Project: GSocket (Generic Socket) for WX
3 * Name: src/mac/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 // ----------------------------------------------------------------------------
19 // Mac-specific data associated with each socket by GSocketCFManager
20 // ----------------------------------------------------------------------------
21
22 class MacGSocketData
23 {
24 public:
25 // default ctor creates the object in uninitialized state, use Initialize()
26 // later to make it usable
27 MacGSocketData()
28 {
29 m_socket = NULL;
30 m_source = NULL;
31 }
32
33 // initialize the data associated with the given socket
34 bool Initialize(GSocket *socket)
35 {
36 wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" );
37
38 // we need a valid Unix socket to create a CFSocket
39 if ( socket->m_fd < 0 )
40 return false;
41
42 CFSocketContext cont;
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
48
49 m_socket = CFSocketCreateWithNative
50 (
51 NULL, // default allocator
52 socket->m_fd,
53 kCFSocketReadCallBack |
54 kCFSocketWriteCallBack |
55 kCFSocketConnectCallBack,
56 SocketCallback,
57 &cont
58 );
59 if ( !m_socket )
60 return false;
61
62 m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
63
64 return m_source != NULL;
65 }
66
67 // free the objects created by Initialize()
68 ~MacGSocketData()
69 {
70 if ( m_source )
71 CFRelease(m_source);
72 if ( m_socket )
73 CFRelease(m_socket);
74 }
75
76 // return true if Initialize() had already been called successfully
77 bool IsInitialized() const { return m_source && m_socket; }
78
79
80 // accessors: should only be called if IsInitialized()
81 CFSocketRef GetSocket() const
82 {
83 wxASSERT( IsInitialized() );
84
85 return m_socket;
86 }
87
88 CFRunLoopSourceRef GetSource() const
89 {
90 wxASSERT( IsInitialized() );
91
92 return m_source;
93 }
94
95 private:
96 static void SocketCallback(CFSocketRef WXUNUSED(s),
97 CFSocketCallBackType callbackType,
98 CFDataRef WXUNUSED(address),
99 const void* data,
100 void* info)
101 {
102 GSocket * const socket = wx_static_cast(GSocket *, info);
103 MacGSocketData * const
104 macdata = wx_static_cast(MacGSocketData *, socket->m_gui_dependent);
105 if ( !macdata )
106 return;
107
108 switch (callbackType)
109 {
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.
118 if (data == NULL)
119 socket->Detected_Write();
120 break;
121
122 case kCFSocketReadCallBack:
123 socket->Detected_Read();
124 break;
125
126 case kCFSocketWriteCallBack:
127 socket->Detected_Write();
128 break;
129
130 default:
131 wxFAIL_MSG( "unexpected socket callback" );
132 }
133 }
134
135 CFSocketRef m_socket;
136 CFRunLoopSourceRef m_source;
137
138 DECLARE_NO_COPY_CLASS(MacGSocketData);
139 };
140
141 // ----------------------------------------------------------------------------
142 // CoreFoundation implementation of GSocketManager
143 // ----------------------------------------------------------------------------
144
145 class GSocketCFManager : public GSocketManager
146 {
147 public:
148 virtual bool OnInit();
149 virtual void OnExit();
150
151 virtual bool Init_Socket(GSocket *socket);
152 virtual void Destroy_Socket(GSocket *socket);
153
154 virtual void Install_Callback(GSocket *socket, GSocketEvent event);
155 virtual void Uninstall_Callback(GSocket *socket, GSocketEvent event);
156
157 virtual void Enable_Events(GSocket *socket);
158 virtual void Disable_Events(GSocket *socket);
159
160 private:
161 // retrieve our custom data associated with the given socket
162 //
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
165 // yet
166 //
167 // may return NULL if we hadn't created the data for this socket yet
168 MacGSocketData *GetData(GSocket *socket) const
169 {
170 return wx_static_cast(MacGSocketData *, socket->m_gui_dependent);
171 }
172
173 // return the custom data pointer initializing it if it hadn't been done
174 // yet
175 //
176 // may return NULL if there is no associated data
177 MacGSocketData *GetInitializedData(GSocket *socket) const
178 {
179 MacGSocketData * const data = GetData(socket);
180 if ( data && !data->IsInitialized() )
181 {
182 if ( !data->Initialize(socket) )
183 return NULL;
184 }
185
186 return data;
187 }
188
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
192 // socket)
193 static int GetCFCallback(GSocket *socket, GSocketEvent event);
194
195
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;
199 };
200
201 CFRunLoopRef GSocketCFManager::ms_mainRunLoop = NULL;
202
203 bool GSocketCFManager::OnInit()
204 {
205 // No need to store the main loop again
206 if (ms_mainRunLoop != NULL)
207 return true;
208
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 )
214 return false;
215
216 CFRetain(ms_mainRunLoop);
217
218 return true;
219 }
220
221 void GSocketCFManager::OnExit()
222 {
223 // Release the reference count, and set the reference back to NULL
224 CFRelease(ms_mainRunLoop);
225 ms_mainRunLoop = NULL;
226 }
227
228 bool GSocketCFManager::Init_Socket(GSocket *socket)
229 {
230 socket->m_gui_dependent = new MacGSocketData;
231 return true;
232 }
233
234 void GSocketCFManager::Destroy_Socket(GSocket *socket)
235 {
236 MacGSocketData * const data = GetData(socket);
237 if ( data )
238 {
239 delete data;
240 socket->m_gui_dependent = NULL;
241 }
242 }
243
244 /* static */
245 int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event)
246 {
247 switch ( event )
248 {
249 case GSOCK_CONNECTION:
250 return socket->m_server ? kCFSocketReadCallBack
251 : kCFSocketConnectCallBack;
252
253 case GSOCK_LOST:
254 case GSOCK_INPUT:
255 return kCFSocketReadCallBack;
256
257 case GSOCK_OUTPUT:
258 return kCFSocketWriteCallBack;
259
260 case GSOCK_MAX_EVENT:
261 wxFAIL_MSG( "invalid GSocketEvent" );
262 return 0;
263
264 default:
265 wxFAIL_MSG( "unknown GSocketEvent" );
266 return 0;
267 }
268 }
269
270 void GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event)
271 {
272 const MacGSocketData * const data = GetInitializedData(socket);
273 if ( !data )
274 return;
275
276 CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
277 }
278
279 void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event)
280 {
281 const MacGSocketData * const data = GetInitializedData(socket);
282 if ( !data )
283 return;
284
285 CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
286 }
287
288 void GSocketCFManager::Enable_Events(GSocket *socket)
289 {
290 const MacGSocketData * const data = GetInitializedData(socket);
291 if ( !data )
292 return;
293
294 CFRunLoopAddSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes);
295 }
296
297 void GSocketCFManager::Disable_Events(GSocket *socket)
298 {
299 const MacGSocketData * const data = GetInitializedData(socket);
300 if ( !data )
301 return;
302
303 // CFSocketInvalidate does CFRunLoopRemoveSource anyway
304 CFRunLoopRemoveSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes);
305 CFSocketInvalidate(data->GetSocket());
306
307 // CFSocketInvalidate has closed the socket so we want to make sure GSocket knows this
308 socket->m_fd = -1;
309 }
310
311 GSocketManager *wxAppTraits::GetSocketManager()
312 {
313 static GSocketCFManager s_manager;
314
315 return &s_manager;
316 };
317
318 #endif // wxUSE_SOCKETS