]>
Commit | Line | Data |
---|---|---|
51fe4b60 VZ |
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 | ///////////////////////////////////////////////////////////////////////////// | |
489468fe SC |
11 | |
12 | #include "wx/wxprec.h" | |
13 | ||
14 | #if wxUSE_SOCKETS | |
15 | ||
60913641 VZ |
16 | #include "wx/private/socket.h" |
17 | #include "wx/unix/private/sockunix.h" | |
489468fe SC |
18 | #include "wx/apptrait.h" |
19 | ||
20 | #include <CoreFoundation/CoreFoundation.h> | |
21 | ||
f0fbbe23 VZ |
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 | ||
489468fe | 33 | // ---------------------------------------------------------------------------- |
51fe4b60 | 34 | // Mac-specific socket implementation |
489468fe SC |
35 | // ---------------------------------------------------------------------------- |
36 | ||
51fe4b60 | 37 | class wxSocketImplMac : public wxSocketImplUnix |
489468fe SC |
38 | { |
39 | public: | |
51fe4b60 VZ |
40 | wxSocketImplMac(wxSocketBase& wxsocket) |
41 | : wxSocketImplUnix(wxsocket) | |
489468fe SC |
42 | { |
43 | m_socket = NULL; | |
44 | m_source = NULL; | |
45 | } | |
46 | ||
51fe4b60 VZ |
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() | |
489468fe | 63 | { |
51fe4b60 VZ |
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); | |
489468fe | 76 | |
51fe4b60 VZ |
77 | CFRelease(m_source); |
78 | CFRelease(m_socket); | |
79 | } | |
80 | ||
81 | // initialize the data associated with the given socket | |
82 | bool Initialize() | |
83 | { | |
489468fe | 84 | // we need a valid Unix socket to create a CFSocket |
51fe4b60 | 85 | if ( m_fd < 0 ) |
489468fe SC |
86 | return false; |
87 | ||
88 | CFSocketContext cont; | |
89 | cont.version = 0; // this currently must be 0 | |
51fe4b60 | 90 | cont.info = this; // pointer passed to our callback |
489468fe SC |
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 | |
51fe4b60 | 98 | m_fd, |
489468fe SC |
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 | ||
f0fbbe23 VZ |
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 | ||
489468fe SC |
121 | static void SocketCallback(CFSocketRef WXUNUSED(s), |
122 | CFSocketCallBackType callbackType, | |
123 | CFDataRef WXUNUSED(address), | |
124 | const void* data, | |
125 | void* info) | |
126 | { | |
51fe4b60 | 127 | wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(info); |
489468fe SC |
128 | |
129 | switch (callbackType) | |
130 | { | |
131 | case kCFSocketConnectCallBack: | |
51566e1f | 132 | wxASSERT(!socket->IsServer()); |
489468fe SC |
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) | |
579213e9 | 140 | socket->OnWriteWaiting(); |
489468fe SC |
141 | break; |
142 | ||
143 | case kCFSocketReadCallBack: | |
579213e9 | 144 | socket->OnReadWaiting(); |
489468fe SC |
145 | break; |
146 | ||
147 | case kCFSocketWriteCallBack: | |
579213e9 | 148 | socket->OnWriteWaiting(); |
489468fe SC |
149 | break; |
150 | ||
151 | default: | |
152 | wxFAIL_MSG( "unexpected socket callback" ); | |
153 | } | |
154 | } | |
155 | ||
156 | CFSocketRef m_socket; | |
157 | CFRunLoopSourceRef m_source; | |
158 | ||
c0c133e1 | 159 | wxDECLARE_NO_COPY_CLASS(wxSocketImplMac); |
489468fe SC |
160 | }; |
161 | ||
f0fbbe23 VZ |
162 | } // anonymous namespace |
163 | ||
164 | ||
489468fe | 165 | // ---------------------------------------------------------------------------- |
51fe4b60 | 166 | // CoreFoundation implementation of wxSocketManager |
489468fe SC |
167 | // ---------------------------------------------------------------------------- |
168 | ||
51fe4b60 | 169 | class wxSocketManagerMac : public wxSocketManager |
489468fe SC |
170 | { |
171 | public: | |
172 | virtual bool OnInit(); | |
173 | virtual void OnExit(); | |
174 | ||
51fe4b60 | 175 | virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket) |
489468fe | 176 | { |
51fe4b60 | 177 | return new wxSocketImplMac(wxsocket); |
489468fe SC |
178 | } |
179 | ||
51fe4b60 VZ |
180 | virtual void Install_Callback(wxSocketImpl *socket, wxSocketNotify event); |
181 | virtual void Uninstall_Callback(wxSocketImpl *socket, wxSocketNotify event); | |
489468fe | 182 | |
51fe4b60 | 183 | private: |
489468fe SC |
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) | |
51fe4b60 | 188 | static int GetCFCallback(wxSocketImpl *socket, wxSocketNotify event); |
489468fe SC |
189 | }; |
190 | ||
51fe4b60 | 191 | bool wxSocketManagerMac::OnInit() |
489468fe SC |
192 | { |
193 | // No need to store the main loop again | |
f0fbbe23 | 194 | if (gs_mainRunLoop != NULL) |
489468fe SC |
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 | |
f0fbbe23 VZ |
200 | gs_mainRunLoop = CFRunLoopGetCurrent(); |
201 | if ( !gs_mainRunLoop ) | |
489468fe SC |
202 | return false; |
203 | ||
f0fbbe23 | 204 | CFRetain(gs_mainRunLoop); |
489468fe SC |
205 | |
206 | return true; | |
207 | } | |
208 | ||
51fe4b60 | 209 | void wxSocketManagerMac::OnExit() |
489468fe SC |
210 | { |
211 | // Release the reference count, and set the reference back to NULL | |
f0fbbe23 VZ |
212 | CFRelease(gs_mainRunLoop); |
213 | gs_mainRunLoop = NULL; | |
489468fe SC |
214 | } |
215 | ||
489468fe | 216 | /* static */ |
51fe4b60 | 217 | int wxSocketManagerMac::GetCFCallback(wxSocketImpl *socket, wxSocketNotify event) |
489468fe SC |
218 | { |
219 | switch ( event ) | |
220 | { | |
51fe4b60 | 221 | case wxSOCKET_CONNECTION: |
51566e1f VZ |
222 | return socket->IsServer() ? kCFSocketReadCallBack |
223 | : kCFSocketConnectCallBack; | |
489468fe | 224 | |
51fe4b60 | 225 | case wxSOCKET_INPUT: |
489468fe SC |
226 | return kCFSocketReadCallBack; |
227 | ||
51fe4b60 | 228 | case wxSOCKET_OUTPUT: |
489468fe SC |
229 | return kCFSocketWriteCallBack; |
230 | ||
c363ead1 VZ |
231 | case wxSOCKET_LOST: |
232 | wxFAIL_MSG( "unexpected wxSocketNotify" ); | |
489468fe SC |
233 | return 0; |
234 | ||
235 | default: | |
51fe4b60 | 236 | wxFAIL_MSG( "unknown wxSocketNotify" ); |
489468fe SC |
237 | return 0; |
238 | } | |
239 | } | |
240 | ||
51fe4b60 VZ |
241 | void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_, |
242 | wxSocketNotify event) | |
489468fe | 243 | { |
51fe4b60 | 244 | wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_); |
489468fe | 245 | |
51fe4b60 | 246 | CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event)); |
489468fe SC |
247 | } |
248 | ||
51fe4b60 VZ |
249 | void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_, |
250 | wxSocketNotify event) | |
489468fe | 251 | { |
51fe4b60 | 252 | wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_); |
489468fe | 253 | |
51fe4b60 | 254 | CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event)); |
489468fe SC |
255 | } |
256 | ||
51fe4b60 VZ |
257 | // set the wxBase variable to point to our wxSocketManager implementation |
258 | // | |
259 | // see comments in wx/apptrait.h for the explanation of why do we do it | |
260 | // like this | |
261 | static struct ManagerSetter | |
489468fe | 262 | { |
51fe4b60 VZ |
263 | ManagerSetter() |
264 | { | |
265 | static wxSocketManagerMac s_manager; | |
266 | wxAppTraits::SetDefaultSocketManager(&s_manager); | |
267 | } | |
268 | } gs_managerSetter; | |
489468fe SC |
269 | |
270 | #endif // wxUSE_SOCKETS |