]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/core/utilsexc_cf.cpp
follow up parent chain to properly support modal dialog parents, see #15383
[wxWidgets.git] / src / osx / core / utilsexc_cf.cpp
index 95f8bd51d2557638146a801c71ed09539d5a50ad..2a8c436223fcf27ee273672f7d30ffeba5b8d716 100644 (file)
@@ -1,11 +1,11 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        src/osx/corefoundation/utilsexec_cf.cpp
+// Name:        src/osx/core/utilsexc_cf.cpp
 // Purpose:     Execution-related utilities for Darwin
 // 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 "wx/thread.h"
 #include "wx/process.h"
 
+#include "wx/evtloop.h"
+#include "wx/evtloopsrc.h"
+#include "wx/private/eventloopsourcesmanager.h"
+
 #include <sys/wait.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)
+#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<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)
+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<void*>(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<int>(callbackType));
+
+    wxCFEventLoopSource * const
+        source = static_cast<wxCFEventLoopSource *>(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<wxCFEventLoopSource>
+            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<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>
+            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