]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/core/utilsexc_cf.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / osx / core / utilsexc_cf.cpp
index 221bbb322d5f399f3ac29b5df7a19157362728b6..2a8c436223fcf27ee273672f7d30ffeba5b8d716 100644 (file)
@@ -4,8 +4,8 @@
 // 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
 /////////////////////////////////////////////////////////////////////////////
@@ -15,7 +15,6 @@
     #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
@@ -157,31 +79,63 @@ public:
         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();
     }
@@ -201,9 +155,19 @@ wxEventLoopSourcesManagerBase* wxGUIAppTraits::GetEventLoopSourcesManager()
 // 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