]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/osx/core/sockosx.cpp
implement wxEventLoop::DispatchTimeout() for wxGTK (thanks Paul) and rewrote it to...
[wxWidgets.git] / src / osx / core / sockosx.cpp
... / ...
CommitLineData
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
22namespace
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
31static CFRunLoopRef gs_mainRunLoop = NULL;
32
33// ----------------------------------------------------------------------------
34// Mac-specific socket implementation
35// ----------------------------------------------------------------------------
36
37class wxSocketImplMac : public wxSocketImplUnix
38{
39public:
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
61private:
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
169class wxSocketManagerMac : public wxSocketManager
170{
171public:
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
183private:
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
191bool 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
209void 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 */
217int 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
242void 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
250void 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
262static 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