]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/gsockosx.cpp
include 'Version' in version string, it's what Apple apps do
[wxWidgets.git] / src / mac / corefoundation / gsockosx.cpp
CommitLineData
02dad85e
DE
1/* -------------------------------------------------------------------------
2 * Project: GSocket (Generic Socket) for WX
0ba6a836 3 * Name: src/mac/corefoundation/gsockosx.c
02dad85e
DE
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
0ba6a836 9#include "wx/wxprec.h"
02dad85e
DE
10
11#if wxUSE_SOCKETS
12
02dad85e 13#include "wx/gsocket.h"
2804f77d 14#include "wx/apptrait.h"
02dad85e
DE
15
16#include <CoreFoundation/CoreFoundation.h>
17
2804f77d
VZ
18// ----------------------------------------------------------------------------
19// Mac-specific data associated with each socket by GSocketCFManager
20// ----------------------------------------------------------------------------
02dad85e 21
2804f77d 22class MacGSocketData
02dad85e 23{
2804f77d
VZ
24public:
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
95private:
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);
02dad85e
DE
139};
140
2804f77d
VZ
141// ----------------------------------------------------------------------------
142// CoreFoundation implementation of GSocketManager
143// ----------------------------------------------------------------------------
7725dc7c 144
2804f77d 145class GSocketCFManager : public GSocketManager
02dad85e 146{
2804f77d
VZ
147public:
148 virtual bool OnInit();
149 virtual void OnExit();
02dad85e 150
2804f77d
VZ
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);
02dad85e 159
2804f77d
VZ
160private:
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 }
0a647691 188
2804f77d
VZ
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
201CFRunLoopRef GSocketCFManager::ms_mainRunLoop = NULL;
202
203bool GSocketCFManager::OnInit()
02dad85e 204{
7725dc7c 205 // No need to store the main loop again
2804f77d 206 if (ms_mainRunLoop != NULL)
7725dc7c
KH
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
2804f77d
VZ
212 ms_mainRunLoop = CFRunLoopGetCurrent();
213 if ( !ms_mainRunLoop )
214 return false;
215
216 CFRetain(ms_mainRunLoop);
7725dc7c 217
0a647691 218 return true;
02dad85e
DE
219}
220
2804f77d 221void GSocketCFManager::OnExit()
02dad85e 222{
7725dc7c 223 // Release the reference count, and set the reference back to NULL
2804f77d
VZ
224 CFRelease(ms_mainRunLoop);
225 ms_mainRunLoop = NULL;
02dad85e
DE
226}
227
2804f77d 228bool GSocketCFManager::Init_Socket(GSocket *socket)
02dad85e 229{
2804f77d
VZ
230 socket->m_gui_dependent = new MacGSocketData;
231 return true;
02dad85e
DE
232}
233
2804f77d 234void GSocketCFManager::Destroy_Socket(GSocket *socket)
02dad85e 235{
2804f77d
VZ
236 MacGSocketData * const data = GetData(socket);
237 if ( data )
02dad85e 238 {
2804f77d
VZ
239 delete data;
240 socket->m_gui_dependent = NULL;
02dad85e
DE
241 }
242}
243
2804f77d
VZ
244/* static */
245int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event)
02dad85e 246{
2804f77d 247 switch ( event )
02dad85e 248 {
2804f77d
VZ
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;
02dad85e 267 }
02dad85e
DE
268}
269
2804f77d 270void GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event)
02dad85e 271{
2804f77d
VZ
272 const MacGSocketData * const data = GetInitializedData(socket);
273 if ( !data )
274 return;
275
276 CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
02dad85e
DE
277}
278
2804f77d 279void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event)
02dad85e 280{
2804f77d
VZ
281 const MacGSocketData * const data = GetInitializedData(socket);
282 if ( !data )
283 return;
02dad85e 284
2804f77d 285 CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event));
02dad85e
DE
286}
287
2804f77d 288void GSocketCFManager::Enable_Events(GSocket *socket)
02dad85e 289{
2804f77d
VZ
290 const MacGSocketData * const data = GetInitializedData(socket);
291 if ( !data )
292 return;
02dad85e 293
2804f77d
VZ
294 CFRunLoopAddSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes);
295}
296
297void 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());
dd6c8be6
DE
306
307 // CFSocketInvalidate has closed the socket so we want to make sure GSocket knows this
2804f77d 308 socket->m_fd = -1;
02dad85e
DE
309}
310
2804f77d
VZ
311GSocketManager *wxAppTraits::GetSocketManager()
312{
313 static GSocketCFManager s_manager;
314
315 return &s_manager;
316};
317
02dad85e 318#endif // wxUSE_SOCKETS