git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57653 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
271 lines
8.3 KiB
C++
271 lines
8.3 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: osx/core/gsockosx.cpp
|
|
// Purpose: wxSocketImpl implementation for OS X
|
|
// Authors: Brian Victor, Vadim Zeitlin
|
|
// Created: February 2002
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2002 Brian Victor
|
|
// (c) 2008 Vadim Zeitlin
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#if wxUSE_SOCKETS
|
|
|
|
#include "wx/private/socket.h"
|
|
#include "wx/unix/private/sockunix.h"
|
|
#include "wx/apptrait.h"
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
namespace
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// global variables
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Sockets must use the event loop to monitor the events so we store a
|
|
// reference to the main thread event loop here
|
|
static CFRunLoopRef gs_mainRunLoop = NULL;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Mac-specific socket implementation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxSocketImplMac : public wxSocketImplUnix
|
|
{
|
|
public:
|
|
wxSocketImplMac(wxSocketBase& wxsocket)
|
|
: wxSocketImplUnix(wxsocket)
|
|
{
|
|
m_socket = NULL;
|
|
m_source = NULL;
|
|
}
|
|
|
|
virtual ~wxSocketImplMac()
|
|
{
|
|
wxASSERT_MSG( !m_source && !m_socket, "forgot to call Close()?" );
|
|
}
|
|
|
|
// get the underlying socket: creates it on demand
|
|
CFSocketRef GetSocket() /* const */
|
|
{
|
|
if ( !m_socket )
|
|
Initialize();
|
|
|
|
return m_socket;
|
|
}
|
|
|
|
private:
|
|
virtual void DoClose()
|
|
{
|
|
wxSocketManager * const manager = wxSocketManager::Get();
|
|
if ( manager )
|
|
{
|
|
manager->Uninstall_Callback(this, wxSOCKET_INPUT);
|
|
manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
|
|
}
|
|
|
|
// VZ: CFRunLoopRemoveSource() is probably unnecessary as
|
|
// CFSocketInvalidate() seems to do it internally from reading the
|
|
// docs, please remove it (and this comment) after testing
|
|
CFRunLoopRemoveSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
|
|
CFSocketInvalidate(m_socket);
|
|
|
|
CFRelease(m_source);
|
|
CFRelease(m_socket);
|
|
}
|
|
|
|
// initialize the data associated with the given socket
|
|
bool Initialize()
|
|
{
|
|
// we need a valid Unix socket to create a CFSocket
|
|
if ( m_fd < 0 )
|
|
return false;
|
|
|
|
CFSocketContext cont;
|
|
cont.version = 0; // this currently must be 0
|
|
cont.info = this; // pointer passed to our callback
|
|
cont.retain = NULL; // no need to retain/release/copy the
|
|
cont.release = NULL; // socket pointer, so all callbacks
|
|
cont.copyDescription = NULL; // can be left NULL
|
|
|
|
m_socket = CFSocketCreateWithNative
|
|
(
|
|
NULL, // default allocator
|
|
m_fd,
|
|
kCFSocketReadCallBack |
|
|
kCFSocketWriteCallBack |
|
|
kCFSocketConnectCallBack,
|
|
SocketCallback,
|
|
&cont
|
|
);
|
|
if ( !m_socket )
|
|
return false;
|
|
|
|
m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0);
|
|
|
|
if ( !m_source )
|
|
{
|
|
CFRelease(m_socket);
|
|
return false;
|
|
}
|
|
|
|
CFRunLoopAddSource(gs_mainRunLoop, m_source, kCFRunLoopCommonModes);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void SocketCallback(CFSocketRef WXUNUSED(s),
|
|
CFSocketCallBackType callbackType,
|
|
CFDataRef WXUNUSED(address),
|
|
const void* data,
|
|
void* info)
|
|
{
|
|
wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(info);
|
|
|
|
switch (callbackType)
|
|
{
|
|
case kCFSocketConnectCallBack:
|
|
wxASSERT(!socket->IsServer());
|
|
// KH: If data is non-NULL, the connect failed, do not call Detected_Write,
|
|
// which will only end up creating a spurious connect event because the
|
|
// call to getsocketopt SO_ERROR inexplicably returns no error.
|
|
// The change in behavior cannot be traced to any particular commit or
|
|
// timeframe so I'm not sure what to think, but after so many hours,
|
|
// this seems to address the issue and it's time to move on.
|
|
if (data == NULL)
|
|
socket->OnWriteWaiting();
|
|
break;
|
|
|
|
case kCFSocketReadCallBack:
|
|
socket->OnReadWaiting();
|
|
break;
|
|
|
|
case kCFSocketWriteCallBack:
|
|
socket->OnWriteWaiting();
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( "unexpected socket callback" );
|
|
}
|
|
}
|
|
|
|
CFSocketRef m_socket;
|
|
CFRunLoopSourceRef m_source;
|
|
|
|
DECLARE_NO_COPY_CLASS(wxSocketImplMac)
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CoreFoundation implementation of wxSocketManager
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxSocketManagerMac : public wxSocketManager
|
|
{
|
|
public:
|
|
virtual bool OnInit();
|
|
virtual void OnExit();
|
|
|
|
virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket)
|
|
{
|
|
return new wxSocketImplMac(wxsocket);
|
|
}
|
|
|
|
virtual void Install_Callback(wxSocketImpl *socket, wxSocketNotify event);
|
|
virtual void Uninstall_Callback(wxSocketImpl *socket, wxSocketNotify event);
|
|
|
|
private:
|
|
// return CFSocket callback mask corresponding to the given event (the
|
|
// socket parameter is needed because some events are interpreted
|
|
// differently depending on whether they happen on a server or on a client
|
|
// socket)
|
|
static int GetCFCallback(wxSocketImpl *socket, wxSocketNotify event);
|
|
};
|
|
|
|
bool wxSocketManagerMac::OnInit()
|
|
{
|
|
// No need to store the main loop again
|
|
if (gs_mainRunLoop != NULL)
|
|
return true;
|
|
|
|
// Get the loop for the main thread so our events will actually fire.
|
|
// The common socket.cpp code will assert if initialize is called from a
|
|
// secondary thread, otherwise Mac would have the same problems as MSW
|
|
gs_mainRunLoop = CFRunLoopGetCurrent();
|
|
if ( !gs_mainRunLoop )
|
|
return false;
|
|
|
|
CFRetain(gs_mainRunLoop);
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxSocketManagerMac::OnExit()
|
|
{
|
|
// Release the reference count, and set the reference back to NULL
|
|
CFRelease(gs_mainRunLoop);
|
|
gs_mainRunLoop = NULL;
|
|
}
|
|
|
|
/* static */
|
|
int wxSocketManagerMac::GetCFCallback(wxSocketImpl *socket, wxSocketNotify event)
|
|
{
|
|
switch ( event )
|
|
{
|
|
case wxSOCKET_CONNECTION:
|
|
return socket->IsServer() ? kCFSocketReadCallBack
|
|
: kCFSocketConnectCallBack;
|
|
|
|
case wxSOCKET_INPUT:
|
|
return kCFSocketReadCallBack;
|
|
|
|
case wxSOCKET_OUTPUT:
|
|
return kCFSocketWriteCallBack;
|
|
|
|
case wxSOCKET_LOST:
|
|
wxFAIL_MSG( "unexpected wxSocketNotify" );
|
|
return 0;
|
|
|
|
default:
|
|
wxFAIL_MSG( "unknown wxSocketNotify" );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void wxSocketManagerMac::Install_Callback(wxSocketImpl *socket_,
|
|
wxSocketNotify event)
|
|
{
|
|
wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
|
|
|
|
CFSocketEnableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
|
|
}
|
|
|
|
void wxSocketManagerMac::Uninstall_Callback(wxSocketImpl *socket_,
|
|
wxSocketNotify event)
|
|
{
|
|
wxSocketImplMac * const socket = static_cast<wxSocketImplMac *>(socket_);
|
|
|
|
CFSocketDisableCallBacks(socket->GetSocket(), GetCFCallback(socket, event));
|
|
}
|
|
|
|
// set the wxBase variable to point to our wxSocketManager implementation
|
|
//
|
|
// see comments in wx/apptrait.h for the explanation of why do we do it
|
|
// like this
|
|
static struct ManagerSetter
|
|
{
|
|
ManagerSetter()
|
|
{
|
|
static wxSocketManagerMac s_manager;
|
|
wxAppTraits::SetDefaultSocketManager(&s_manager);
|
|
}
|
|
} gs_managerSetter;
|
|
|
|
#endif // wxUSE_SOCKETS
|