1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Execution-related utilities 
   4 // Author:      Stefan Csomor 
   5 // Modified by: David Elliott 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:       wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  15 #include "wx/unix/execute.h" 
  19 #include <mach/mach.h> 
  21 #include <CoreFoundation/CFMachPort.h> 
  29 #define wxEXECUTE_WIN_MESSAGE 10000 
  31 #include "wx/mac/private.h" 
  34 Below FinderLaunch function comes from: 
  35 http://developer.apple.com/technotes/tn/tn1002.html#fndrask 
  37     /* FinderLaunch converts a list of nTargets FSSpec records 
  38     pointed to by the targetList parameter and converts the 
  39     list to an Apple Event.  It then sends that event to the 
  40     Finder.  The array of FSSpec records pointed to by the 
  41     targetList parameter may contain references to files, 
  42     folders, or applications.  The net effect of this command 
  43     is equivalent to the user selecting an icon in one of the 
  44     Finder's windows and then choosing the open command from 
  45     the Finder's file menu. */ 
  46 static OSErr 
FinderLaunch(long nTargets
, FSSpec 
*targetList
) { 
  48     AppleEvent theAEvent
, theReply
; 
  49     AEAddressDesc fndrAddress
; 
  50     AEDescList targetListDesc
; 
  53     AliasHandle targetAlias
; 
  57     AECreateDesc(typeNull
, NULL
, 0, &theAEvent
); 
  58     AECreateDesc(typeNull
, NULL
, 0, &fndrAddress
); 
  59     AECreateDesc(typeNull
, NULL
, 0, &theReply
); 
  60     AECreateDesc(typeNull
, NULL
, 0, &targetListDesc
); 
  64         /* verify parameters */ 
  65     if ((nTargets 
== 0) || (targetList 
== NULL
)) { 
  70         /* create an open documents event targeting the 
  72     err 
= AECreateDesc(typeApplSignature
, (Ptr
) &fndrCreator
, 
  73         sizeof(fndrCreator
), &fndrAddress
); 
  74     if (err 
!= noErr
) goto bail
; 
  75     err 
= AECreateAppleEvent(kCoreEventClass
, kAEOpenDocuments
, 
  76         &fndrAddress
, kAutoGenerateReturnID
, 
  77         kAnyTransactionID
, &theAEvent
); 
  78     if (err 
!= noErr
) goto bail
; 
  80         /* create the list of files to open */ 
  81     err 
= AECreateList(NULL
, 0, false, &targetListDesc
); 
  82     if (err 
!= noErr
) goto bail
; 
  83     for ( index
=0; index 
< nTargets
; index
++) { 
  84         if (targetAlias 
== NULL
) 
  85             err 
= NewAlias(NULL
, (targetList 
+ index
), 
  87         else err 
= UpdateAlias(NULL
, (targetList 
+ index
), 
  88                    targetAlias
, &wasChanged
); 
  89         if (err 
!= noErr
) goto bail
; 
  90         HLock((Handle
) targetAlias
); 
  91         err 
= AEPutPtr(&targetListDesc
, (index 
+ 1), 
  92               typeAlias
, *targetAlias
, 
  93               GetHandleSize((Handle
) targetAlias
)); 
  94         HUnlock((Handle
) targetAlias
); 
  95         if (err 
!= noErr
) goto bail
; 
  98         /* add the file list to the Apple Event */ 
  99     err 
= AEPutParamDesc(&theAEvent
, keyDirectObject
, 
 101     if (err 
!= noErr
) goto bail
; 
 103         /* send the event to the Finder */ 
 104     err 
= AESend(&theAEvent
, &theReply
, kAENoReply
, 
 105         kAENormalPriority
, kAEDefaultTimeout
, NULL
, NULL
); 
 107         /* clean up and leave */ 
 109     if (targetAlias 
!= NULL
) DisposeHandle((Handle
) targetAlias
); 
 110     AEDisposeDesc(&targetListDesc
); 
 111     AEDisposeDesc(&theAEvent
); 
 112     AEDisposeDesc(&fndrAddress
); 
 113     AEDisposeDesc(&theReply
); 
 117 long wxExecute(const wxString
& command
, int flags
, wxProcess 
*WXUNUSED(handler
)) 
 119     wxASSERT_MSG( flags 
== wxEXEC_ASYNC
, 
 120         wxT("wxExecute: Only wxEXEC_ASYNC is supported") ); 
 123     wxMacFilename2FSSpec(command
, &fsSpec
); 
 125     // 0 means execution failed. Returning non-zero is a PID, but not 
 126     // on Mac where PIDs are 64 bits and won't fit in a long, so we 
 127     // return a dummy value for now. 
 128     return ( FinderLaunch(1 /*one file*/, &fsSpec
) == noErr 
) ? -1 : 0; 
 134 void wxMAC_MachPortEndProcessDetect(CFMachPortRef port
, void *data
) 
 136         wxEndProcessData 
*proc_data 
= (wxEndProcessData
*)data
; 
 137         wxLogDebug(wxT("Wow.. this actually worked!")); 
 139         int rc 
= waitpid(abs(proc_data
->pid
), &status
, WNOHANG
); 
 142                 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!")); 
 145         if((rc 
!= -1) && WIFEXITED(status
)) 
 146                 proc_data
->exitcode 
= WEXITSTATUS(status
); 
 148                 proc_data
->exitcode 
= -1; 
 149         wxHandleProcessTermination(proc_data
); 
 152 int wxAddProcessCallbackForPid(wxEndProcessData 
*proc_data
, int pid
) 
 156     kern_return_t    kernResult
; 
 157     mach_port_t    taskOfOurProcess
; 
 158     mach_port_t    machPortForProcess
; 
 159     taskOfOurProcess 
= mach_task_self(); 
 160     if(taskOfOurProcess 
== MACH_PORT_NULL
) 
 162         wxLogDebug(wxT("No mach_task_self()")); 
 165     wxLogDebug(wxT("pid=%d"),pid
); 
 166     kernResult 
= task_for_pid(taskOfOurProcess
,pid
, &machPortForProcess
); 
 167     if(kernResult 
!= KERN_SUCCESS
) 
 169         wxLogDebug(wxT("no task_for_pid()")); 
 170         // try seeing if it is already dead or something 
 171         // FIXME: a better method would be to call the callback function 
 172         // from idle time until the process terminates. Of course, how 
 173         // likely is it that it will take more than 0.1 seconds for the 
 174         // mach terminate event to make its way to the BSD subsystem? 
 175         usleep(100); // sleep for 0.1 seconds 
 176         wxMAC_MachPortEndProcessDetect(NULL
, (void*)proc_data
); 
 179     CFMachPortContext termcb_contextinfo
; 
 180     termcb_contextinfo
.version 
= NULL
; 
 181     termcb_contextinfo
.info 
= (void*)proc_data
; 
 182     termcb_contextinfo
.retain 
= NULL
; 
 183     termcb_contextinfo
.release 
= NULL
; 
 184     termcb_contextinfo
.copyDescription 
= NULL
; 
 185     CFMachPortRef    CFMachPortForProcess
; 
 186     Boolean        ShouldFreePort
; 
 187     CFMachPortForProcess 
= CFMachPortCreateWithPort(NULL
, machPortForProcess
, NULL
, &termcb_contextinfo
, &ShouldFreePort
); 
 188     if(!CFMachPortForProcess
) 
 190         wxLogDebug(wxT("No CFMachPortForProcess")); 
 191         mach_port_deallocate(taskOfOurProcess
, machPortForProcess
); 
 196         kernResult 
= mach_port_deallocate(taskOfOurProcess
, machPortForProcess
); 
 197         if(kernResult
!=KERN_SUCCESS
) 
 199             wxLogDebug(wxT("Couldn't deallocate mach port")); 
 203     CFMachPortSetInvalidationCallBack(CFMachPortForProcess
, &wxMAC_MachPortEndProcessDetect
); 
 204     CFRunLoopSourceRef    runloopsource
; 
 205     runloopsource 
= CFMachPortCreateRunLoopSource(NULL
,CFMachPortForProcess
, (CFIndex
)0); 
 208         wxLogDebug(wxT("Couldn't create runloopsource")); 
 212     CFRelease(CFMachPortForProcess
); 
 214     CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource
,kCFRunLoopDefaultMode
); 
 215     CFRelease(runloopsource
); 
 216     wxLogDebug(wxT("Successfully added notification to the runloop"));