// Author: David Elliott, Ryan Norton (wxMacExecute)
// Modified by: Stefan Csomor (added necessary wxT for unicode builds)
// Created: 2004-11-04
-// RCS-ID: $Id$
// Copyright: (c) David Elliott, Ryan Norton
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
// Notes: This code comes from src/osx/carbon/utilsexc.cpp,1.11
/////////////////////////////////////////////////////////////////////////////
#include "wx/log.h"
#include "wx/utils.h"
#endif //ndef WX_PRECOMP
-#include "wx/unix/execute.h"
#include "wx/stdpaths.h"
#include "wx/app.h"
#include "wx/apptrait.h"
#include <sys/wait.h>
-#include <CoreFoundation/CFFileDescriptor.h>
#include <CoreFoundation/CFSocket.h>
-/*!
- Called due to source signal detected by the CFRunLoop.
- This is nearly identical to the wxGTK equivalent.
- */
-extern "C" void WXCF_EndProcessDetector(CFSocketRef s,
- CFSocketCallBackType WXUNUSED(callbackType),
- CFDataRef WXUNUSED(address),
- void const *WXUNUSED(data),
- void *info)
-{
- /*
- Either our pipe was closed or the process ended successfully. Either way,
- we're done. It's not if waitpid is going to magically succeed when
- we get fired again. CFSocketInvalidate closes the fd for us and also
- invalidates the run loop source for us which should cause it to
- release the CFSocket (thus causing it to be deallocated) and remove
- itself from the runloop which should release it and cause it to also
- be deallocated. Of course, it's possible the RunLoop hangs onto
- one or both of them by retaining/releasing them within its stack
- frame. However, that shouldn't be depended on. Assume that s is
- deallocated due to the following call.
- */
- CFSocketInvalidate(s);
-
- // Now tell wx that the process has ended.
- wxHandleProcessTermination(static_cast<wxEndProcessData *>(info));
-}
-
-/*!
- Implements the GUI-specific AddProcessCallback() for both wxMac and
- wxCocoa using the CFSocket/CFRunLoop API which is available to both.
- Takes advantage of the fact that sockets on UNIX are just regular
- file descriptors and thus even a non-socket file descriptor can
- apparently be used with CFSocket so long as you only tell CFSocket
- to do things with it that would be valid for a non-socket fd.
- */
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- static int s_last_tag = 0;
- CFSocketContext context =
- { 0
- , static_cast<void*>(proc_data)
- , NULL
- , NULL
- , NULL
- };
- CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
- if(cfSocket == NULL)
- {
- wxLogError(wxT("Failed to create socket for end process detection"));
- return 0;
- }
- CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
- if(runLoopSource == NULL)
- {
- wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection"));
- // closes the fd.. we can't really stop it, nor do we necessarily want to.
- CFSocketInvalidate(cfSocket);
- CFRelease(cfSocket);
- return 0;
- }
- // Now that the run loop source has the socket retained and we no longer
- // need to refer to it within this method, we can release it.
- CFRelease(cfSocket);
-
- CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
- // Now that the run loop has the source retained we can release it.
- CFRelease(runLoopSource);
-
- /*
- Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
- itself into our callback and that's enough to be able to
- CFSocketInvalidate it which is all we need to do to get everything we
- just created to be deallocated.
- */
- return ++s_last_tag;
-}
-
#if wxUSE_EVENTLOOP_SOURCE
namespace
{
-void EnableDescriptorCallBacks(CFFileDescriptorRef cffd, int flags)
-{
- if ( flags & wxEVENT_SOURCE_INPUT )
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
- if ( flags & wxEVENT_SOURCE_OUTPUT )
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack);
-}
-
+extern "C"
void
-wx_cffiledescriptor_callback(CFFileDescriptorRef cffd,
- CFOptionFlags flags,
- void *ctxData)
+wx_socket_callback(CFSocketRef WXUNUSED(s),
+ CFSocketCallBackType callbackType,
+ CFDataRef WXUNUSED(address),
+ void const *WXUNUSED(data),
+ void *ctxData)
{
wxLogTrace(wxTRACE_EVT_SOURCE,
- "CFFileDescriptor callback, flags=%d", flags);
+ "CFSocket callback, type=%d", static_cast<int>(callbackType));
wxCFEventLoopSource * const
source = static_cast<wxCFEventLoopSource *>(ctxData);
wxEventLoopSourceHandler * const
handler = source->GetHandler();
- if ( flags & kCFFileDescriptorReadCallBack )
- handler->OnReadWaiting();
- if ( flags & kCFFileDescriptorWriteCallBack )
- handler->OnWriteWaiting();
- // we need to re-enable callbacks to be called again
- EnableDescriptorCallBacks(cffd, source->GetFlags());
+ switch ( callbackType )
+ {
+ case kCFSocketReadCallBack:
+ handler->OnReadWaiting();
+ break;
+
+ case kCFSocketWriteCallBack:
+ handler->OnWriteWaiting();
+ break;
+
+ default:
+ wxFAIL_MSG( "Unexpected callback type." );
+ }
}
} // anonymous namespace
wxScopedPtr<wxCFEventLoopSource>
source(new wxCFEventLoopSource(handler, flags));
- CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL };
- wxCFRef<CFFileDescriptorRef>
- cffd(CFFileDescriptorCreate
- (
- kCFAllocatorDefault,
- fd,
- true, // close on invalidate
- wx_cffiledescriptor_callback,
- &ctx
- ));
- if ( !cffd )
+ CFSocketContext context = { 0, source.get(), NULL, NULL, NULL };
+
+ int callbackTypes = 0;
+ if ( flags & wxEVENT_SOURCE_INPUT )
+ callbackTypes |= kCFSocketReadCallBack;
+ if ( flags & wxEVENT_SOURCE_OUTPUT )
+ callbackTypes |= kCFSocketWriteCallBack;
+
+ wxCFRef<CFSocketRef>
+ cfSocket(CFSocketCreateWithNative
+ (
+ kCFAllocatorDefault,
+ fd,
+ callbackTypes,
+ &wx_socket_callback,
+ &context
+ ));
+
+ if ( !cfSocket )
+ {
+ wxLogError(wxS("Failed to create event loop source socket."));
return NULL;
+ }
+
+ // Adjust the socket options to suit our needs:
+ CFOptionFlags sockopt = CFSocketGetSocketFlags(cfSocket);
+
+ // First, by default, write callback is not called repeatedly when data
+ // can be written to the socket but we need this behaviour so request
+ // it explicitly.
+ if ( flags & wxEVENT_SOURCE_OUTPUT )
+ sockopt |= kCFSocketAutomaticallyReenableWriteCallBack;
+
+ // Second, we use the socket to monitor the FD but it doesn't own it,
+ // so prevent the FD from being closed when the socket is invalidated.
+ sockopt &= ~kCFSocketCloseOnInvalidate;
+
+ CFSocketSetSocketFlags(cfSocket, sockopt);
wxCFRef<CFRunLoopSourceRef>
- cfsrc(CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0));
- if ( !cfsrc )
+ runLoopSource(CFSocketCreateRunLoopSource
+ (
+ kCFAllocatorDefault,
+ cfSocket,
+ 0 // Lowest index means highest priority
+ ));
+ if ( !runLoopSource )
+ {
+ wxLogError(wxS("Failed to create low level event loop source."));
+ CFSocketInvalidate(cfSocket);
return NULL;
+ }
- CFRunLoopRef cfloop = CFRunLoopGetCurrent();
- CFRunLoopAddSource(cfloop, cfsrc, kCFRunLoopDefaultMode);
-
- // Enable the callbacks initially.
- EnableDescriptorCallBacks(cffd, source->GetFlags());
+ // Save the socket so that we can remove it later if asked to.
+ source->InitSourceSocket(cfSocket.release());
- source->SetFileDescriptor(cffd.release());
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
return source.release();
}
// NOTE: This doesn't really belong here but this was a handy file to
// put it in because it's already compiled for wxCocoa and wxMac GUI lib.
#if wxUSE_STDPATHS
-static wxStandardPathsCF gs_stdPaths;
wxStandardPaths& wxGUIAppTraits::GetStandardPaths()
{
+ // Derive a class just to be able to create it: wxStandardPaths ctor is
+ // protected to prevent its misuse, but it also means we can't create an
+ // object of this class directly.
+ class wxStandardPathsDefault : public wxStandardPathsCF
+ {
+ public:
+ wxStandardPathsDefault() { }
+ };
+
+ static wxStandardPathsDefault gs_stdPaths;
+
return gs_stdPaths;
}
#endif