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"));