]> git.saurik.com Git - wxWidgets.git/blob - src/osx/core/sockosx.cpp
Don't forget to reset wxSocketImplMac members to NULL.
[wxWidgets.git] / src / osx / core / sockosx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: osx/core/gsockosx.cpp
3 // Purpose: wxSocketImpl implementation for OS X
4 // Authors: Brian Victor, Vadim Zeitlin
5 // Created: February 2002
6 // RCS-ID: $Id$
7 // Copyright: (c) 2002 Brian Victor
8 // (c) 2008 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_SOCKETS
15
16 #include "wx/private/socket.h"
17 #include "wx/unix/private/sockunix.h"
18 #include "wx/apptrait.h"
19
20 #include <CoreFoundation/CoreFoundation.h>
21
22 namespace
23 {
24
25 // ----------------------------------------------------------------------------
26 // global variables
27 // ----------------------------------------------------------------------------
28
29 // Sockets must use the event loop to monitor the events so we store a
30 // reference to the main thread event loop here
31 static CFRunLoopRef gs_mainRunLoop = NULL;
32
33 // ----------------------------------------------------------------------------
34 // Mac-specific socket implementation
35 // ----------------------------------------------------------------------------
36
37 class wxSocketImplMac : public wxSocketImplUnix
38 {
39 public:
40 wxSocketImplMac(wxSocketBase& wxsocket)
41 : wxSocketImplUnix(wxsocket)
42 {
43 m_socket = NULL;
44 m_source = NULL;
45 }
46
47 virtual ~wxSocketImplMac()
48 {
49 wxASSERT_MSG( !m_source && !m_socket, "forgot to call Close()?" );
50 }
51
52 // get the underlying socket: creates it on demand
53 CFSocketRef GetSocket() /* const */
54 {
55 if ( !m_socket )
56 Initialize();
57
58 return m_socket;
59 }
60
61 private:
62 virtual void DoClose()
63 {
64 wxSocketManager * const manager = wxSocketManager::Get();
65 if ( manager )
66 {
67 manager->Uninstall_Callback(this, wxSOCKET_INPUT);
68 manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
69 }
70
71 // VZ: CFRunLoopRemoveSource() is probably unnecessary as
72 // CFSocketInvalidate() seems to do it internally from reading the
73 // docs, please remove it (and this comment) after testing
74 CFRunLoopRemoveSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
75 CFSocketInvalidate(m_socket);
76
77 CFRelease(m_source);
78 m_source = NULL;
79
80 CFRelease(m_socket);
81 m_socket = NULL;
82 }
83
84 // initialize the data associated with the given socket
85 bool Initialize()
86 {
87 // we need a valid Unix socket to create a CFSocket
88 if ( m_fd < 0 )
89 return false;
90
91 CFSocketContext cont;
92 cont.version = 0; // this currently must be 0
93 cont.info = this; // pointer passed to our callback
94 cont.retain = NULL; // no need to retain/release/copy the
95 cont.release = NULL; // socket pointer, so all callbacks
96 cont.copyDescription = NULL; // can be left NULL
97
98 m_socket = CFSocketCreateWithNative
99 (
100 NULL, // default allocator
101 m_fd,
102 kCFSocketReadCallBack |
103 kCFSocketWriteCallBack |
104 kCFSocketConnectCallBack,
105 SocketCallback,
106 &cont
107 );
108 if ( !m_socket )
109 return false;
110
111 m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
112
113 if ( !m_source )
114 {
115 CFRelease(m_socket);
116 m_socket = NULL;
117
118 return false;
119 }
120
121 CFRunLoopAddSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
122
123 return true;
124 }
125
126 static void SocketCallback(CFSocketRef WXUNUSED(s),
127 CFSocketCallBackType callbackType,
128 CFDataRef WXUNUSED(address),
129 const void* data,
130 void* info)
131 {
132 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(info);
133
134 switch (callbackType)
135 {
136 case kCFSocketConnectCallBack:
137 wxASSERT(!socket->IsServer());
138 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
139 // which will only end up creating a spurious connect event because the
140 // call to getsocketopt SO_ERROR inexplicably returns no error.
141 // The change in behavior cannot be traced to any particular commit or
142 // timeframe so I'm not sure what to think, but after so many hours,
143 // this seems to address the issue and it's time to move on.
144 if (data == NULL)
145 socket->OnWriteWaiting();
146 break;
147
148 case kCFSocketReadCallBack:
149 socket->OnReadWaiting();
150 break;
151
152 case kCFSocketWriteCallBack:
153 socket->OnWriteWaiting();
154 break;
155
156 default:
157 wxFAIL_MSG( "unexpected socket callback" );
158 }
159 }
160
161 CFSocketRef m_socket;
162 CFRunLoopSourceRef m_source;
163
164 wxDECLARE_NO_COPY_CLASS(wxSocketImplMac);
165 };
166
167 } // anonymous namespace
168
169
170 // ----------------------------------------------------------------------------
171 // CoreFoundation implementation of wxSocketManager
172 // ----------------------------------------------------------------------------
173
174 class wxSocketManagerMac : public wxSocketManager
175 {
176 public:
177 virtual bool OnInit();
178 virtual void OnExit();
179
180 virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket)
181 {
182 return new wxSocketImplMac(wxsocket);
183 }
184
185 virtual void Install_Callback(wxSocketImpl *socket, wxSocketNotify event);
186 virtual void Uninstall_Callback(wxSocketImpl *socket, wxSocketNotify event);
187
188 private:
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(wxSocketImpl *socket, wxSocketNotify event);
194 };
195
196 bool wxSocketManagerMac::OnInit()
197 {
198 // No need to store the main loop again
199 if (gs_mainRunLoop != NULL)
200 return true;
201
202 // Get the loop for the main thread so our events will actually fire.
203 // The common socket.cpp code will assert if initialize is called from a
204 // secondary thread, otherwise Mac would have the same problems as MSW
205 gs_mainRunLoop = CFRunLoopGetCurrent();
206 if ( !gs_mainRunLoop )
207 return false;
208
209 CFRetain(gs_mainRunLoop);
210
211 return true;
212 }
213
214 void wxSocketManagerMac::OnExit()
215 {
216 // Release the reference count, and set the reference back to NULL
217 CFRelease(gs_mainRunLoop);
218 gs_mainRunLoop = NULL;
219 }
220
221 /* static */
222 int wxSocketManagerMac::GetCFCallback(wxSocketImpl *socket, wxSocketNotify event)
223 {
224 switch ( event )
225 {
226 case wxSOCKET_CONNECTION:
227 return socket->IsServer() ? kCFSocketReadCallBack
228 : kCFSocketConnectCallBack;
229
230 case wxSOCKET_INPUT:
231 return kCFSocketReadCallBack;
232
233 case wxSOCKET_OUTPUT:
234 return kCFSocketWriteCallBack;
235
236 case wxSOCKET_LOST:
237 wxFAIL_MSG( "unexpected wxSocketNotify" );
238 return 0;
239
240 default:
241 wxFAIL_MSG( "unknown wxSocketNotify" );
242 return 0;
243 }
244 }
245
246 void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_,
247 wxSocketNotify event)
248 {
249 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
250
251 CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
252 }
253
254 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_,
255 wxSocketNotify event)
256 {
257 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
258
259 CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
260 }
261
262 // set the wxBase variable to point to CF wxSocketManager implementation so
263 // that the GUI code in utilsexc_cf.cpp could return it from its traits method
264 //
265 // this is very roundabout but necessary to allow us to have different
266 // behaviours in console and GUI applications while avoiding dependencies of
267 // GUI library on the network one
268 extern WXDLLIMPEXP_BASE wxSocketManager *wxOSXSocketManagerCF;
269
270 static struct OSXManagerSetter
271 {
272 OSXManagerSetter()
273 {
274 static wxSocketManagerMac s_manager;
275 wxOSXSocketManagerCF = &s_manager;
276 }
277 } gs_OSXManagerSetter;
278
279 #endif // wxUSE_SOCKETS