]> git.saurik.com Git - wxWidgets.git/blob - src/mac/classic/utilsexc.cpp
Restore a use case that was lost with the last set of updates, using a
[wxWidgets.git] / src / mac / classic / utilsexc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/classic/utilsexec.cpp
3 // Purpose: Execution-related utilities
4 // Author: Stefan Csomor
5 // Modified by: David Elliott
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #include "wx/utils.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/log.h"
18 #endif
19
20 #ifdef __DARWIN__
21 #include "wx/unix/execute.h"
22 #include <unistd.h>
23 #include <sys/wait.h>
24 extern "C" {
25 #include <mach/mach.h>
26 }
27 #include <CoreFoundation/CFMachPort.h>
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #ifndef __DARWIN__
35 #define wxEXECUTE_WIN_MESSAGE 10000
36
37 #include "wx/mac/private.h"
38
39 /*
40 Below FinderLaunch function comes from:
41 http://developer.apple.com/technotes/tn/tn1002.html#fndrask
42 */
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) {
53 OSErr err;
54 AppleEvent theAEvent, theReply;
55 AEAddressDesc fndrAddress;
56 AEDescList targetListDesc;
57 OSType fndrCreator;
58 Boolean wasChanged;
59 AliasHandle targetAlias;
60 long index;
61
62 /* set up locals */
63 AECreateDesc(typeNull, NULL, 0, &theAEvent);
64 AECreateDesc(typeNull, NULL, 0, &fndrAddress);
65 AECreateDesc(typeNull, NULL, 0, &theReply);
66 AECreateDesc(typeNull, NULL, 0, &targetListDesc);
67 targetAlias = NULL;
68 fndrCreator = 'MACS';
69
70 /* verify parameters */
71 if ((nTargets == 0) || (targetList == NULL)) {
72 err = paramErr;
73 goto bail;
74 }
75
76 /* create an open documents event targeting the
77 finder */
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;
85
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),
92 &targetAlias);
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;
102 }
103
104 /* add the file list to the Apple Event */
105 err = AEPutParamDesc(&theAEvent, keyDirectObject,
106 &targetListDesc);
107 if (err != noErr) goto bail;
108
109 /* send the event to the Finder */
110 err = AESend(&theAEvent, &theReply, kAENoReply,
111 kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
112
113 /* clean up and leave */
114 bail:
115 if (targetAlias != NULL) DisposeHandle((Handle) targetAlias);
116 AEDisposeDesc(&targetListDesc);
117 AEDisposeDesc(&theAEvent);
118 AEDisposeDesc(&fndrAddress);
119 AEDisposeDesc(&theReply);
120 return err;
121 }
122
123 long wxExecute(const wxString& command, int flags, wxProcess *WXUNUSED(handler))
124 {
125 wxASSERT_MSG( flags == wxEXEC_ASYNC,
126 wxT("wxExecute: Only wxEXEC_ASYNC is supported") );
127
128 FSSpec fsSpec;
129 wxMacFilename2FSSpec(command, &fsSpec);
130
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;
135 }
136
137 #endif
138
139 #ifdef __DARWIN__
140 void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
141 {
142 wxEndProcessData *proc_data = (wxEndProcessData*)data;
143 wxLogDebug(wxT("Wow.. this actually worked!"));
144 int status = 0;
145 int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
146 if(!rc)
147 {
148 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
149 return;
150 }
151 if((rc != -1) && WIFEXITED(status))
152 proc_data->exitcode = WEXITSTATUS(status);
153 else
154 proc_data->exitcode = -1;
155 wxHandleProcessTermination(proc_data);
156 }
157
158 int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
159 {
160 if(pid < 1)
161 return -1;
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)
167 {
168 wxLogDebug(wxT("No mach_task_self()"));
169 return -1;
170 }
171 wxLogDebug(wxT("pid=%d"),pid);
172 kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
173 if(kernResult != KERN_SUCCESS)
174 {
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);
183 return -1;
184 }
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)
195 {
196 wxLogDebug(wxT("No CFMachPortForProcess"));
197 mach_port_deallocate(taskOfOurProcess, machPortForProcess);
198 return -1;
199 }
200 if(ShouldFreePort)
201 {
202 kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
203 if(kernResult!=KERN_SUCCESS)
204 {
205 wxLogDebug(wxT("Couldn't deallocate mach port"));
206 return -1;
207 }
208 }
209 CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
210 CFRunLoopSourceRef runloopsource;
211 runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
212 if(!runloopsource)
213 {
214 wxLogDebug(wxT("Couldn't create runloopsource"));
215 return -1;
216 }
217
218 CFRelease(CFMachPortForProcess);
219
220 CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
221 CFRelease(runloopsource);
222 wxLogDebug(wxT("Successfully added notification to the runloop"));
223 return 0;
224 }
225 #endif