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 /////////////////////////////////////////////////////////////////////////////
13 //#pragma implementation
19 #include "wx/unix/execute.h"
23 #include <mach/mach.h>
25 #include <CoreFoundation/CFMachPort.h>
33 #define wxEXECUTE_WIN_MESSAGE 10000
35 #include "wx/mac/private.h"
38 Below FinderLaunch function comes from:
39 http://developer.apple.com/technotes/tn/tn1002.html#fndrask
41 /* FinderLaunch converts a list of nTargets FSSpec records
42 pointed to by the targetList parameter and converts the
43 list to an Apple Event. It then sends that event to the
44 Finder. The array of FSSpec records pointed to by the
45 targetList parameter may contain references to files,
46 folders, or applications. The net effect of this command
47 is equivalent to the user selecting an icon in one of the
48 Finder's windows and then choosing the open command from
49 the Finder's file menu. */
50 static OSErr
FinderLaunch(long nTargets
, FSSpec
*targetList
) {
52 AppleEvent theAEvent
, theReply
;
53 AEAddressDesc fndrAddress
;
54 AEDescList targetListDesc
;
57 AliasHandle targetAlias
;
61 AECreateDesc(typeNull
, NULL
, 0, &theAEvent
);
62 AECreateDesc(typeNull
, NULL
, 0, &fndrAddress
);
63 AECreateDesc(typeNull
, NULL
, 0, &theReply
);
64 AECreateDesc(typeNull
, NULL
, 0, &targetListDesc
);
68 /* verify parameters */
69 if ((nTargets
== 0) || (targetList
== NULL
)) {
74 /* create an open documents event targeting the
76 err
= AECreateDesc(typeApplSignature
, (Ptr
) &fndrCreator
,
77 sizeof(fndrCreator
), &fndrAddress
);
78 if (err
!= noErr
) goto bail
;
79 err
= AECreateAppleEvent(kCoreEventClass
, kAEOpenDocuments
,
80 &fndrAddress
, kAutoGenerateReturnID
,
81 kAnyTransactionID
, &theAEvent
);
82 if (err
!= noErr
) goto bail
;
84 /* create the list of files to open */
85 err
= AECreateList(NULL
, 0, false, &targetListDesc
);
86 if (err
!= noErr
) goto bail
;
87 for ( index
=0; index
< nTargets
; index
++) {
88 if (targetAlias
== NULL
)
89 err
= NewAlias(NULL
, (targetList
+ index
),
91 else err
= UpdateAlias(NULL
, (targetList
+ index
),
92 targetAlias
, &wasChanged
);
93 if (err
!= noErr
) goto bail
;
94 HLock((Handle
) targetAlias
);
95 err
= AEPutPtr(&targetListDesc
, (index
+ 1),
96 typeAlias
, *targetAlias
,
97 GetHandleSize((Handle
) targetAlias
));
98 HUnlock((Handle
) targetAlias
);
99 if (err
!= noErr
) goto bail
;
102 /* add the file list to the Apple Event */
103 err
= AEPutParamDesc(&theAEvent
, keyDirectObject
,
105 if (err
!= noErr
) goto bail
;
107 /* send the event to the Finder */
108 err
= AESend(&theAEvent
, &theReply
, kAENoReply
,
109 kAENormalPriority
, kAEDefaultTimeout
, NULL
, NULL
);
111 /* clean up and leave */
113 if (targetAlias
!= NULL
) DisposeHandle((Handle
) targetAlias
);
114 AEDisposeDesc(&targetListDesc
);
115 AEDisposeDesc(&theAEvent
);
116 AEDisposeDesc(&fndrAddress
);
117 AEDisposeDesc(&theReply
);
121 long wxExecute(const wxString
& command
, int flags
, wxProcess
*WXUNUSED(handler
))
123 wxASSERT_MSG( flags
== wxEXEC_ASYNC
,
124 wxT("wxExecute: Only wxEXEC_ASYNC is supported") );
127 wxMacFilename2FSSpec(command
, &fsSpec
);
129 // 0 means execution failed. Returning non-zero is a PID, but not
130 // on Mac where PIDs are 64 bits and won't fit in a long, so we
131 // return a dummy value for now.
132 return ( FinderLaunch(1 /*one file*/, &fsSpec
) == noErr
) ? -1 : 0;
138 void wxMAC_MachPortEndProcessDetect(CFMachPortRef port
, void *data
)
140 wxEndProcessData
*proc_data
= (wxEndProcessData
*)data
;
141 wxLogDebug(wxT("Wow.. this actually worked!"));
143 int rc
= waitpid(abs(proc_data
->pid
), &status
, WNOHANG
);
146 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
149 if((rc
!= -1) && WIFEXITED(status
))
150 proc_data
->exitcode
= WEXITSTATUS(status
);
152 proc_data
->exitcode
= -1;
153 wxHandleProcessTermination(proc_data
);
156 int wxAddProcessCallbackForPid(wxEndProcessData
*proc_data
, int pid
)
160 kern_return_t kernResult
;
161 mach_port_t taskOfOurProcess
;
162 mach_port_t machPortForProcess
;
163 taskOfOurProcess
= mach_task_self();
164 if(taskOfOurProcess
== MACH_PORT_NULL
)
166 wxLogDebug(wxT("No mach_task_self()"));
169 wxLogDebug(wxT("pid=%d"),pid
);
170 kernResult
= task_for_pid(taskOfOurProcess
,pid
, &machPortForProcess
);
171 if(kernResult
!= KERN_SUCCESS
)
173 wxLogDebug(wxT("no task_for_pid()"));
174 // try seeing if it is already dead or something
175 // FIXME: a better method would be to call the callback function
176 // from idle time until the process terminates. Of course, how
177 // likely is it that it will take more than 0.1 seconds for the
178 // mach terminate event to make its way to the BSD subsystem?
179 usleep(100); // sleep for 0.1 seconds
180 wxMAC_MachPortEndProcessDetect(NULL
, (void*)proc_data
);
183 CFMachPortContext termcb_contextinfo
;
184 termcb_contextinfo
.version
= NULL
;
185 termcb_contextinfo
.info
= (void*)proc_data
;
186 termcb_contextinfo
.retain
= NULL
;
187 termcb_contextinfo
.release
= NULL
;
188 termcb_contextinfo
.copyDescription
= NULL
;
189 CFMachPortRef CFMachPortForProcess
;
190 Boolean ShouldFreePort
;
191 CFMachPortForProcess
= CFMachPortCreateWithPort(NULL
, machPortForProcess
, NULL
, &termcb_contextinfo
, &ShouldFreePort
);
192 if(!CFMachPortForProcess
)
194 wxLogDebug(wxT("No CFMachPortForProcess"));
195 mach_port_deallocate(taskOfOurProcess
, machPortForProcess
);
200 kernResult
= mach_port_deallocate(taskOfOurProcess
, machPortForProcess
);
201 if(kernResult
!=KERN_SUCCESS
)
203 wxLogDebug(wxT("Couldn't deallocate mach port"));
207 CFMachPortSetInvalidationCallBack(CFMachPortForProcess
, &wxMAC_MachPortEndProcessDetect
);
208 CFRunLoopSourceRef runloopsource
;
209 runloopsource
= CFMachPortCreateRunLoopSource(NULL
,CFMachPortForProcess
, (CFIndex
)0);
212 wxLogDebug(wxT("Couldn't create runloopsource"));
216 CFRelease(CFMachPortForProcess
);
218 CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource
,kCFRunLoopDefaultMode
);
219 CFRelease(runloopsource
);
220 wxLogDebug(wxT("Successfully added notification to the runloop"));