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