]> git.saurik.com Git - wxWidgets.git/blame - src/osx/core/sockosx.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / osx / core / sockosx.cpp
CommitLineData
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
24namespace
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
33static CFRunLoopRef gs_mainRunLoop = NULL;
34
489468fe 35// ----------------------------------------------------------------------------
51fe4b60 36// Mac-specific socket implementation
489468fe
SC
37// ----------------------------------------------------------------------------
38
51fe4b60 39class wxSocketImplMac : public wxSocketImplUnix
489468fe
SC
40{
41public:
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
63private:
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 183class wxSocketManagerMac : public wxSocketManager
489468fe
SC
184{
185public:
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 197private:
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 205bool 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 223void 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 231int 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
255void 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
263void 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
277extern WXDLLIMPEXP_BASE wxSocketManager *wxOSXSocketManagerCF;
278
279static 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 289wxFORCE_LINK_THIS_MODULE(osxsocket)
3270038f 290
489468fe 291#endif // wxUSE_SOCKETS