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