X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/80fdcdb90ef779185492dab676d461fc34933312..27d79a5027bee4f46e57c813d072422065cb1592:/src/osx/core/utilsexc_cf.cpp diff --git a/src/osx/core/utilsexc_cf.cpp b/src/osx/core/utilsexc_cf.cpp index fe010f63be..b9fae2e446 100644 --- a/src/osx/core/utilsexc_cf.cpp +++ b/src/osx/core/utilsexc_cf.cpp @@ -6,6 +6,7 @@ // 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 ///////////////////////////////////////////////////////////////////////////// @@ -15,103 +16,159 @@ #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 "wx/thread.h" #include "wx/process.h" +#include "wx/evtloop.h" +#include "wx/evtloopsrc.h" +#include "wx/private/eventloopsourcesmanager.h" + #include #include -/*! - 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) +#if wxUSE_EVENTLOOP_SOURCE + +namespace { - /* - 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(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) +extern "C" +void +wx_socket_callback(CFSocketRef WXUNUSED(s), + CFSocketCallBackType callbackType, + CFDataRef WXUNUSED(address), + void const *WXUNUSED(data), + void *ctxData) { - static int s_last_tag = 0; - CFSocketContext context = - { 0 - , static_cast(proc_data) - , NULL - , NULL - , NULL - }; - CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context); - if(cfSocket == NULL) + wxLogTrace(wxTRACE_EVT_SOURCE, + "CFSocket callback, type=%d", static_cast(callbackType)); + + wxCFEventLoopSource * const + source = static_cast(ctxData); + + wxEventLoopSourceHandler * const + handler = source->GetHandler(); + + switch ( callbackType ) { - wxLogError(wxT("Failed to create socket for end process detection")); - return 0; + case kCFSocketReadCallBack: + handler->OnReadWaiting(); + break; + + case kCFSocketWriteCallBack: + handler->OnWriteWaiting(); + break; + + default: + wxFAIL_MSG( "Unexpected callback type." ); } - CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0); - if(runLoopSource == NULL) +} + +} // anonymous namespace + +class wxCFEventLoopSourcesManager : public wxEventLoopSourcesManagerBase +{ +public: + wxEventLoopSource * + AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags) { - 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; + wxCHECK_MSG( fd != -1, NULL, "can't monitor invalid fd" ); + + wxScopedPtr + source(new wxCFEventLoopSource(handler, flags)); + + 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 + 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 + 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; + } + + // Save the socket so that we can remove it later if asked to. + source->InitSourceSocket(cfSocket.release()); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); + + return source.release(); } - // 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; +}; + +wxEventLoopSourcesManagerBase* wxGUIAppTraits::GetEventLoopSourcesManager() +{ + static wxCFEventLoopSourcesManager s_eventLoopSourcesManager; + + return &s_eventLoopSourcesManager; } +#endif // wxUSE_EVENTLOOP_SOURCE + ///////////////////////////////////////////////////////////////////////////// // 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