]> git.saurik.com Git - wxWidgets.git/blob - src/osx/core/sockosx.cpp
Use CF socket manager in GUI OS X applications.
[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 CFRelease(m_socket);
79 }
80
81 // initialize the data associated with the given socket
82 bool Initialize()
83 {
84 // we need a valid Unix socket to create a CFSocket
85 if ( m_fd < 0 )
86 return false;
87
88 CFSocketContext cont;
89 cont.version = 0; // this currently must be 0
90 cont.info = this; // pointer passed to our callback
91 cont.retain = NULL; // no need to retain/release/copy the
92 cont.release = NULL; // socket pointer, so all callbacks
93 cont.copyDescription = NULL; // can be left NULL
94
95 m_socket = CFSocketCreateWithNative
96 (
97 NULL, // default allocator
98 m_fd,
99 kCFSocketReadCallBack |
100 kCFSocketWriteCallBack |
101 kCFSocketConnectCallBack,
102 SocketCallback,
103 &cont
104 );
105 if ( !m_socket )
106 return false;
107
108 m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
109
110 if ( !m_source )
111 {
112 CFRelease(m_socket);
113 return false;
114 }
115
116 CFRunLoopAddSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
117
118 return true;
119 }
120
121 static void SocketCallback(CFSocketRef WXUNUSED(s),
122 CFSocketCallBackType callbackType,
123 CFDataRef WXUNUSED(address),
124 const void* data,
125 void* info)
126 {
127 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(info);
128
129 switch (callbackType)
130 {
131 case kCFSocketConnectCallBack:
132 wxASSERT(!socket->IsServer());
133 // KH: If data is non-NULL, the connect failed, do not call Detected_Write,
134 // which will only end up creating a spurious connect event because the
135 // call to getsocketopt SO_ERROR inexplicably returns no error.
136 // The change in behavior cannot be traced to any particular commit or
137 // timeframe so I'm not sure what to think, but after so many hours,
138 // this seems to address the issue and it's time to move on.
139 if (data == NULL)
140 socket->OnWriteWaiting();
141 break;
142
143 case kCFSocketReadCallBack:
144 socket->OnReadWaiting();
145 break;
146
147 case kCFSocketWriteCallBack:
148 socket->OnWriteWaiting();
149 break;
150
151 default:
152 wxFAIL_MSG( "unexpected socket callback" );
153 }
154 }
155
156 CFSocketRef m_socket;
157 CFRunLoopSourceRef m_source;
158
159 wxDECLARE_NO_COPY_CLASS(wxSocketImplMac);
160 };
161
162 } // anonymous namespace
163
164
165 // ----------------------------------------------------------------------------
166 // CoreFoundation implementation of wxSocketManager
167 // ----------------------------------------------------------------------------
168
169 class wxSocketManagerMac : public wxSocketManager
170 {
171 public:
172 virtual bool OnInit();
173 virtual void OnExit();
174
175 virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket)
176 {
177 return new wxSocketImplMac(wxsocket);
178 }
179
180 virtual void Install_Callback(wxSocketImpl *socket, wxSocketNotify event);
181 virtual void Uninstall_Callback(wxSocketImpl *socket, wxSocketNotify event);
182
183 private:
184 // return CFSocket callback mask corresponding to the given event (the
185 // socket parameter is needed because some events are interpreted
186 // differently depending on whether they happen on a server or on a client
187 // socket)
188 static int GetCFCallback(wxSocketImpl *socket, wxSocketNotify event);
189 };
190
191 bool wxSocketManagerMac::OnInit()
192 {
193 // No need to store the main loop again
194 if (gs_mainRunLoop != NULL)
195 return true;
196
197 // Get the loop for the main thread so our events will actually fire.
198 // The common socket.cpp code will assert if initialize is called from a
199 // secondary thread, otherwise Mac would have the same problems as MSW
200 gs_mainRunLoop = CFRunLoopGetCurrent();
201 if ( !gs_mainRunLoop )
202 return false;
203
204 CFRetain(gs_mainRunLoop);
205
206 return true;
207 }
208
209 void wxSocketManagerMac::OnExit()
210 {
211 // Release the reference count, and set the reference back to NULL
212 CFRelease(gs_mainRunLoop);
213 gs_mainRunLoop = NULL;
214 }
215
216 /* static */
217 int wxSocketManagerMac::GetCFCallback(wxSocketImpl *socket, wxSocketNotify event)
218 {
219 switch ( event )
220 {
221 case wxSOCKET_CONNECTION:
222 return socket->IsServer() ? kCFSocketReadCallBack
223 : kCFSocketConnectCallBack;
224
225 case wxSOCKET_INPUT:
226 return kCFSocketReadCallBack;
227
228 case wxSOCKET_OUTPUT:
229 return kCFSocketWriteCallBack;
230
231 case wxSOCKET_LOST:
232 wxFAIL_MSG( "unexpected wxSocketNotify" );
233 return 0;
234
235 default:
236 wxFAIL_MSG( "unknown wxSocketNotify" );
237 return 0;
238 }
239 }
240
241 void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_,
242 wxSocketNotify event)
243 {
244 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
245
246 CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
247 }
248
249 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_,
250 wxSocketNotify event)
251 {
252 wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
253
254 CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
255 }
256
257 // set the wxBase variable to point to CF wxSocketManager implementation so
258 // that the GUI code in utilsexc_cf.cpp could return it from its traits method
259 //
260 // this is very roundabout but necessary to allow us to have different
261 // behaviours in console and GUI applications while avoiding dependencies of
262 // GUI library on the network one
263 extern WXDLLIMPEXP_BASE wxSocketManager *wxOSXSocketManagerCF;
264
265 static struct OSXManagerSetter
266 {
267 OSXManagerSetter()
268 {
269 static wxSocketManagerMac s_manager;
270 wxOSXSocketManagerCF = &s_manager;
271 }
272 } gs_OSXManagerSetter;
273
274 #endif // wxUSE_SOCKETS