X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/62705a278a57b8d3dd67222f20b161584a86982d..409e6ce4dcd39c6dfe8b77cbd56d451ffe5c731c:/src/mac/corefoundation/utilsexc_cf.cpp?ds=inline diff --git a/src/mac/corefoundation/utilsexc_cf.cpp b/src/mac/corefoundation/utilsexc_cf.cpp index bb6e19d6ed..2d95454fe9 100644 --- a/src/mac/corefoundation/utilsexc_cf.cpp +++ b/src/mac/corefoundation/utilsexc_cf.cpp @@ -17,215 +17,102 @@ #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" -#ifdef __WXCOCOA__ -#include -#include -#else -#include "wx/mac/private.h" -#include "LaunchServices.h" -#endif - -#include "wx/uri.h" -#include "wx/mac/corefoundation/cfstring.h" - -long wxMacExecute(wxChar **argv, - int flags, - wxProcess *process) -{ - CFIndex cfiCount = 0; - //get count - for(wxChar** argvcopy = argv; *argvcopy != NULL ; ++argvcopy) - { - ++cfiCount; - } - - if(cfiCount == 0) //no file to launch? - { - wxLogDebug(wxT("wxMacExecute No file to launch!")); - return -1; - } - - CFURLRef cfurlApp = CFURLCreateWithString( - kCFAllocatorDefault, - wxMacCFStringHolder(*argv++, wxLocale::GetSystemEncoding()), - NULL); - wxASSERT(cfurlApp); - - CFBundleRef cfbApp = CFBundleCreate(kCFAllocatorDefault, cfurlApp); - if(!cfbApp) - { - wxLogDebug(wxT("wxMacExecute Bad bundle")); - CFRelease(cfurlApp); - return -1; - } - - - UInt32 dwBundleType, dwBundleCreator; - CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator); - - //Only call wxMacExecute for .app bundles - others could be actual unix programs - if(dwBundleType != 'APPL') - { - CFRelease(cfurlApp); - return -1; - } - - // - // We have a good bundle - so let's launch it! - // - - CFMutableArrayRef cfaFiles = CFArrayCreateMutable(kCFAllocatorDefault, cfiCount - 1, NULL); - - wxASSERT(cfaFiles); - - if(--cfiCount) - { - for( ; *argv != NULL ; ++argv) - { -// wxLogDebug(*argv); - wxString sCurrentFile; - - if(wxURI(*argv).IsReference()) - sCurrentFile = wxString(wxT("file://")) + *argv; - else - sCurrentFile = *argv; - - CFURLRef cfurlCurrentFile = CFURLCreateWithString( - kCFAllocatorDefault, - wxMacCFStringHolder(sCurrentFile, wxLocale::GetSystemEncoding()), - NULL); - wxASSERT(cfurlCurrentFile); - - CFArrayAppendValue( - cfaFiles, - cfurlCurrentFile - ); - } - } - - LSLaunchURLSpec launchspec; - launchspec.appURL = cfurlApp; - launchspec.itemURLs = cfaFiles; - launchspec.passThruParams = NULL; //AEDesc* - launchspec.launchFlags = kLSLaunchDefaults | kLSLaunchDontSwitch; //TODO: Possibly be smarter with flags - launchspec.asyncRefCon = NULL; - - OSStatus status = LSOpenFromURLSpec(&launchspec, - NULL); //2nd is CFURLRef* really launched - - //cleanup - CFRelease(cfurlApp); - CFRelease(cfaFiles); - - //check for error - if(status != noErr) - { - wxLogDebug(wxString::Format(wxT("wxMacExecute ERROR: %d")), (int)status); - return -1; - } - return 0; //success -} - - -#include #include -extern "C" { -#include -} -void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data) +#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) { - wxEndProcessData *proc_data = (wxEndProcessData*)data; - wxLogDebug(wxT("Process ended")); - int status = 0; - int rc = waitpid(abs(proc_data->pid), &status, WNOHANG); - if(!rc) - { - wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!")); - return; - } - if((rc != -1) && WIFEXITED(status)) - proc_data->exitcode = WEXITSTATUS(status); - else - proc_data->exitcode = -1; - wxHandleProcessTermination(proc_data); + /* + 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)); } -int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid) +/*! + 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) { - if(pid < 1) - return -1; - kern_return_t kernResult; - mach_port_t taskOfOurProcess; - mach_port_t machPortForProcess; - taskOfOurProcess = mach_task_self(); - if(taskOfOurProcess == MACH_PORT_NULL) - { - wxLogDebug(wxT("No mach_task_self()")); - return -1; - } - wxLogDebug(wxT("pid=%d"),pid); - kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess); - if(kernResult != KERN_SUCCESS) - { - wxLogDebug(wxT("no task_for_pid()")); - // try seeing if it is already dead or something - // FIXME: a better method would be to call the callback function - // from idle time until the process terminates. Of course, how - // likely is it that it will take more than 0.1 seconds for the - // mach terminate event to make its way to the BSD subsystem? - usleep(100); // sleep for 0.1 seconds - wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data); - return -1; - } - CFMachPortContext termcb_contextinfo; - termcb_contextinfo.version = 0; - termcb_contextinfo.info = (void*)proc_data; - termcb_contextinfo.retain = NULL; - termcb_contextinfo.release = NULL; - termcb_contextinfo.copyDescription = NULL; - CFMachPortRef CFMachPortForProcess; - Boolean ShouldFreePort; - CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort); - if(!CFMachPortForProcess) - { - wxLogDebug(wxT("No CFMachPortForProcess")); - mach_port_deallocate(taskOfOurProcess, machPortForProcess); - return -1; - } - if(ShouldFreePort) + 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) { - kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess); - if(kernResult!=KERN_SUCCESS) - { - wxLogDebug(wxT("Couldn't deallocate mach port")); - return -1; - } + wxLogError(wxT("Failed to create socket for end process detection")); + return 0; } - CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect); - CFRunLoopSourceRef runloopsource; - runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0); - if(!runloopsource) + CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0); + if(runLoopSource == NULL) { - wxLogDebug(wxT("Couldn't create runloopsource")); - return -1; + 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; } - - CFRelease(CFMachPortForProcess); - - CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode); - CFRelease(runloopsource); - wxLogDebug(wxT("Successfully added notification to the runloop")); - 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; } -// NOTE: This doens't really belong here but this was a handy file to +///////////////////////////////////////////////////////////////////////////// + +// 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; wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths() { return gs_stdPaths; } +#endif