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