]>
Commit | Line | Data |
---|---|---|
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->m_server); | |
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 | DECLARE_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->m_server ? kCFSocketReadCallBack | |
223 | : kCFSocketConnectCallBack; | |
224 | ||
225 | case wxSOCKET_LOST: | |
226 | case wxSOCKET_INPUT: | |
227 | return kCFSocketReadCallBack; | |
228 | ||
229 | case wxSOCKET_OUTPUT: | |
230 | return kCFSocketWriteCallBack; | |
231 | ||
232 | case wxSOCKET_MAX_EVENT: | |
233 | wxFAIL_MSG( "invalid wxSocketNotify" ); | |
234 | return 0; | |
235 | ||
236 | default: | |
237 | wxFAIL_MSG( "unknown wxSocketNotify" ); | |
238 | return 0; | |
239 | } | |
240 | } | |
241 | ||
242 | void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_, | |
243 | wxSocketNotify event) | |
244 | { | |
245 | wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_); | |
246 | ||
247 | CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event)); | |
248 | } | |
249 | ||
250 | void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_, | |
251 | wxSocketNotify event) | |
252 | { | |
253 | wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_); | |
254 | ||
255 | CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event)); | |
256 | } | |
257 | ||
258 | // set the wxBase variable to point to our wxSocketManager implementation | |
259 | // | |
260 | // see comments in wx/apptrait.h for the explanation of why do we do it | |
261 | // like this | |
262 | static struct ManagerSetter | |
263 | { | |
264 | ManagerSetter() | |
265 | { | |
266 | static wxSocketManagerMac s_manager; | |
267 | wxAppTraits::SetDefaultSocketManager(&s_manager); | |
268 | } | |
269 | } gs_managerSetter; | |
270 | ||
271 | #endif // wxUSE_SOCKETS |