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