1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/core/gsockosx.cpp 
   3 // Purpose:     wxSocketImpl implementation for OS X 
   4 // Authors:     Brian Victor, Vadim Zeitlin 
   5 // Created:     February 2002 
   6 // Copyright:   (c) 2002 Brian Victor 
   7 //              (c) 2008 Vadim Zeitlin 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 #include "wx/wxprec.h" 
  15 #include "wx/private/socket.h" 
  16 #include "wx/unix/private/sockunix.h" 
  17 #include "wx/apptrait.h" 
  20 #include "wx/osx/core/cfstring.h"           // for wxMacWakeUp() only 
  22 #include <CoreFoundation/CoreFoundation.h> 
  27 // ---------------------------------------------------------------------------- 
  29 // ---------------------------------------------------------------------------- 
  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
; 
  35 // ---------------------------------------------------------------------------- 
  36 // Mac-specific socket implementation 
  37 // ---------------------------------------------------------------------------- 
  39 class wxSocketImplMac 
: public wxSocketImplUnix
 
  42     wxSocketImplMac(wxSocketBase
& wxsocket
) 
  43         : wxSocketImplUnix(wxsocket
) 
  49     virtual ~wxSocketImplMac() 
  51         wxASSERT_MSG( !m_source 
&& !m_socket
, "forgot to call Close()?" ); 
  54     // get the underlying socket: creates it on demand 
  55     CFSocketRef 
GetSocket() /* const */ 
  64     virtual void DoClose() 
  66         wxSocketManager 
* const manager 
= wxSocketManager::Get(); 
  69             manager
->Uninstall_Callback(this, wxSOCKET_INPUT
); 
  70             manager
->Uninstall_Callback(this, wxSOCKET_OUTPUT
); 
  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
); 
  86     // initialize the data associated with the given socket 
  89         // we need a valid Unix socket to create a CFSocket 
  94         cont
.version 
= 0;               // this currently must be 0 
  95         cont
.info 
= this;               // pointer passed to our callback 
  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 
 100         m_socket 
= CFSocketCreateWithNative
 
 102                         NULL
,                   // default allocator 
 104                         kCFSocketReadCallBack 
| 
 105                         kCFSocketWriteCallBack 
| 
 106                         kCFSocketConnectCallBack
, 
 113         m_source 
= CFSocketCreateRunLoopSource(NULL
, m_socket
, 0); 
 123         CFRunLoopAddSource(gs_mainRunLoop
, m_source
, kCFRunLoopCommonModes
); 
 128     static void SocketCallback(CFSocketRef 
WXUNUSED(s
), 
 129                                CFSocketCallBackType callbackType
, 
 130                                CFDataRef 
WXUNUSED(address
), 
 134         wxSocketImplMac 
* const socket 
= static_cast<wxSocketImplMac 
*>(info
); 
 136         switch (callbackType
) 
 138             case kCFSocketConnectCallBack
: 
 139                 wxASSERT(!socket
->IsServer()); 
 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. 
 143                 // The change in behaviour cannot be traced to any particular commit or 
 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. 
 147                     socket
->OnWriteWaiting(); 
 150             case kCFSocketReadCallBack
: 
 151                 socket
->OnReadWaiting(); 
 154             case kCFSocketWriteCallBack
: 
 155                 socket
->OnWriteWaiting(); 
 159                 wxFAIL_MSG( "unexpected socket callback" ); 
 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 
 170     CFSocketRef m_socket
; 
 171     CFRunLoopSourceRef m_source
; 
 173     wxDECLARE_NO_COPY_CLASS(wxSocketImplMac
); 
 176 } // anonymous namespace 
 179 // ---------------------------------------------------------------------------- 
 180 // CoreFoundation implementation of wxSocketManager 
 181 // ---------------------------------------------------------------------------- 
 183 class wxSocketManagerMac 
: public wxSocketManager
 
 186     virtual bool OnInit(); 
 187     virtual void OnExit(); 
 189     virtual wxSocketImpl 
*CreateSocket(wxSocketBase
& wxsocket
) 
 191         return new wxSocketImplMac(wxsocket
); 
 194     virtual void Install_Callback(wxSocketImpl 
*socket
, wxSocketNotify event
); 
 195     virtual void Uninstall_Callback(wxSocketImpl 
*socket
, wxSocketNotify event
); 
 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 
 202     static int GetCFCallback(wxSocketImpl 
*socket
, wxSocketNotify event
); 
 205 bool wxSocketManagerMac::OnInit() 
 207     // No need to store the main loop again 
 208     if (gs_mainRunLoop 
!= NULL
) 
 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 
 214     gs_mainRunLoop 
= CFRunLoopGetCurrent(); 
 215     if ( !gs_mainRunLoop 
) 
 218     CFRetain(gs_mainRunLoop
); 
 223 void wxSocketManagerMac::OnExit() 
 225     // Release the reference count, and set the reference back to NULL 
 226     CFRelease(gs_mainRunLoop
); 
 227     gs_mainRunLoop 
= NULL
; 
 231 int wxSocketManagerMac::GetCFCallback(wxSocketImpl 
*socket
, wxSocketNotify event
) 
 235         case wxSOCKET_CONNECTION
: 
 236             return socket
->IsServer() ? kCFSocketReadCallBack
 
 237                                       : kCFSocketConnectCallBack
; 
 240             return kCFSocketReadCallBack
; 
 242         case wxSOCKET_OUTPUT
: 
 243             return kCFSocketWriteCallBack
; 
 246             wxFAIL_MSG( "unexpected wxSocketNotify" ); 
 250             wxFAIL_MSG( "unknown wxSocketNotify" ); 
 255 void wxSocketManagerMac::Install_Callback(wxSocketImpl 
*socket_
, 
 256                                           wxSocketNotify event
) 
 258     wxSocketImplMac 
* const socket 
= static_cast<wxSocketImplMac 
*>(socket_
); 
 260     CFSocketEnableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
)); 
 263 void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl 
*socket_
, 
 264                                             wxSocketNotify event
) 
 266     wxSocketImplMac 
* const socket 
= static_cast<wxSocketImplMac 
*>(socket_
); 
 268     CFSocketDisableCallBacks(socket
->GetSocket(), GetCFCallback(socket
, event
)); 
 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 
 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
; 
 279 static struct OSXManagerSetter
 
 283         static wxSocketManagerMac s_manager
; 
 284         wxOSXSocketManagerCF 
= &s_manager
; 
 286 } gs_OSXManagerSetter
; 
 288 // see the relative linker macro in socket.cpp 
 289 wxFORCE_LINK_THIS_MODULE(osxsocket
) 
 291 #endif // wxUSE_SOCKETS