]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/utilsexc_cf.cpp
corrected GetKeyInfo() (bug 1815516)
[wxWidgets.git] / src / mac / corefoundation / utilsexc_cf.cpp
CommitLineData
5d553c56
DE
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/mac/corefoundation/utilsexec_cf.cpp
3// Purpose: Execution-related utilities for Darwin
62705a27 4// Author: David Elliott, Ryan Norton (wxMacExecute)
5d553c56
DE
5// Modified by: Stefan Csomor (added necessary wxT for unicode builds)
6// Created: 2004-11-04
7// RCS-ID: $Id$
62705a27 8// Copyright: (c) David Elliott, Ryan Norton
24498521 9// Licence: wxWindows licence
5d553c56
DE
10// Notes: This code comes from src/mac/carbon/utilsexc.cpp,1.11
11/////////////////////////////////////////////////////////////////////////////
12
13#include "wx/wxprec.h"
14#ifndef WX_PRECOMP
15 #include "wx/log.h"
16 #include "wx/utils.h"
17#endif //ndef WX_PRECOMP
18#include "wx/unix/execute.h"
fc480dc1
DE
19#include "wx/stdpaths.h"
20#include "wx/apptrait.h"
110ffa16
JS
21#include "wx/thread.h"
22#include "wx/process.h"
5d553c56 23
f523d7ce
VZ
24#include <sys/wait.h>
25
110ffa16
JS
26// Use polling instead of Mach ports, which doesn't work on Intel
27// due to task_for_pid security issues.
62705a27 28
653a54d0
SC
29// http://developer.apple.com/technotes/tn/tn2050.html
30
110ffa16
JS
31// What's a better test for Intel vs PPC?
32#ifdef WORDS_BIGENDIAN
33#define USE_POLLING 0
34#else
35#define USE_POLLING 1
36#endif
37
38#if USE_POLLING
39
4fa167e6 40#if wxUSE_THREADS
110ffa16
JS
41class wxProcessTerminationEventHandler: public wxEvtHandler
42{
43 public:
44 wxProcessTerminationEventHandler(wxEndProcessData* data)
45 {
46 m_data = data;
47 Connect(-1, wxEVT_END_PROCESS, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate));
48 }
49
50 void OnTerminate(wxProcessEvent& event)
51 {
52 Disconnect(-1, wxEVT_END_PROCESS, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate));
53 wxHandleProcessTermination(m_data);
54 delete this;
55 }
56
57 wxEndProcessData* m_data;
58};
59
60class wxProcessTerminationThread: public wxThread
61{
62 public:
63 wxProcessTerminationThread(wxEndProcessData* data, wxProcessTerminationEventHandler* handler): wxThread(wxTHREAD_DETACHED)
64 {
65 m_data = data;
66 m_handler = handler;
67 }
68
69 virtual void* Entry();
70
71 wxProcessTerminationEventHandler* m_handler;
72 wxEndProcessData* m_data;
73};
74
75// The problem with this is that we may be examining the
76// process e.g. in OnIdle at the point this cleans up the process,
77// so we need to delay until it's safe.
78
79void* wxProcessTerminationThread::Entry()
80{
81 while (true)
82 {
83 usleep(100);
84 int status = 0;
eff41cb9 85 int rc = waitpid(abs(m_data->pid), & status, 0);
110ffa16
JS
86 if (rc != 0)
87 {
88 if ((rc != -1) && WIFEXITED(status))
89 m_data->exitcode = WEXITSTATUS(status);
90 else
91 m_data->exitcode = -1;
92
93 wxProcessEvent event;
94 wxPostEvent(m_handler, event);
95
96 break;
97 }
98 }
9e34f56b 99
110ffa16
JS
100 return NULL;
101}
102
103int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
104{
105 if (pid < 1)
106 return -1;
107
9e34f56b 108 wxProcessTerminationEventHandler* handler = new wxProcessTerminationEventHandler(proc_data);
110ffa16 109 wxProcessTerminationThread* thread = new wxProcessTerminationThread(proc_data, handler);
9e34f56b 110
110ffa16
JS
111 if (thread->Create() != wxTHREAD_NO_ERROR)
112 {
113 wxLogDebug(wxT("Could not create termination detection thread."));
114 delete thread;
115 delete handler;
116 return -1;
117 }
118
119 thread->Run();
9e34f56b 120
110ffa16
JS
121 return 0;
122}
4fa167e6
PC
123#else // !wxUSE_THREADS
124int wxAddProcessCallbackForPid(wxEndProcessData*, int)
125{
126 wxLogDebug(wxT("Could not create termination detection thread."));
127 return -1;
128}
129#endif // wxUSE_THREADS/!wxUSE_THREADS
110ffa16 130
f523d7ce 131#else // !USE_POLLING
62705a27 132
5d553c56 133#include <CoreFoundation/CFMachPort.h>
5d553c56
DE
134extern "C" {
135#include <mach/mach.h>
136}
137
138void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
139{
24498521 140 wxEndProcessData *proc_data = (wxEndProcessData*)data;
3bdb628b 141 wxLogDebug(wxT("Process ended"));
24498521
DE
142 int status = 0;
143 int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
144 if(!rc)
145 {
f523d7ce
VZ
146 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
147 return;
24498521
DE
148 }
149 if((rc != -1) && WIFEXITED(status))
f523d7ce 150 proc_data->exitcode = WEXITSTATUS(status);
24498521 151 else
f523d7ce 152 proc_data->exitcode = -1;
24498521 153 wxHandleProcessTermination(proc_data);
5d553c56
DE
154}
155
156int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
157{
158 if(pid < 1)
159 return -1;
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)
165 {
166 wxLogDebug(wxT("No mach_task_self()"));
167 return -1;
168 }
169 wxLogDebug(wxT("pid=%d"),pid);
170 kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
171 if(kernResult != KERN_SUCCESS)
172 {
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);
181 return -1;
182 }
183 CFMachPortContext termcb_contextinfo;
b409fa19 184 termcb_contextinfo.version = 0;
5d553c56
DE
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)
193 {
194 wxLogDebug(wxT("No CFMachPortForProcess"));
195 mach_port_deallocate(taskOfOurProcess, machPortForProcess);
196 return -1;
197 }
198 if(ShouldFreePort)
199 {
200 kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
201 if(kernResult!=KERN_SUCCESS)
202 {
203 wxLogDebug(wxT("Couldn't deallocate mach port"));
204 return -1;
205 }
206 }
207 CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
208 CFRunLoopSourceRef runloopsource;
209 runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
210 if(!runloopsource)
211 {
212 wxLogDebug(wxT("Couldn't create runloopsource"));
213 return -1;
214 }
9e34f56b 215
5d553c56
DE
216 CFRelease(CFMachPortForProcess);
217
218 CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
219 CFRelease(runloopsource);
220 wxLogDebug(wxT("Successfully added notification to the runloop"));
221 return 0;
222}
223
f523d7ce 224#endif // USE_POLLING/!USE_POLLING
110ffa16 225
a3261ffb
DE
226/////////////////////////////////////////////////////////////////////////////
227// New implementation avoiding mach ports entirely.
228
229#include <CoreFoundation/CFSocket.h>
230
231/*!
232 Called due to source signal detected by the CFRunLoop.
233 This is nearly identical to the wxGTK equivalent.
234 */
235extern "C" void WXCF_EndProcessDetector(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, void const *data, void *info)
236{
237 wxEndProcessData * const proc_data = static_cast<wxEndProcessData*>(info);
238
239/// This code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
240 // PID is always positive on UNIX but wx uses the sign bit as a flag.
241 int pid = (proc_data->pid > 0) ? proc_data->pid : -proc_data->pid;
242 int status = 0;
243 int rc = waitpid(pid, &status, WNOHANG);
244 if(rc == 0)
245 {
246 // Keep waiting in case we got a spurious notification
247 // NOTE: In my limited testing, this doesn't happen.
248 return;
249 }
250
251 if(rc == -1)
252 { // Error.. really shouldn't happen but try to gracefully handle it
253 wxLogLastError(_T("waitpid"));
254 proc_data->exitcode = -1;
255 }
256 else
257 { // Process ended for some reason
258 wxASSERT_MSG(rc == pid, _T("unexpected waitpid() return value"));
259
260 if(WIFEXITED(status))
261 proc_data->exitcode = WEXITSTATUS(status);
262 else if(WIFSIGNALED(status))
263 // wxGTK doesn't do this but why not?
264 proc_data->exitcode = -WTERMSIG(status);
265 else
266 { // Should NEVER happen according to waitpid docs
267 wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling"));
268 proc_data->exitcode = -1;
269 }
270 }
271/// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
272
273 /*
274 Either waitpid failed or the process ended successfully. Either way,
275 we're done. It's not if waitpid is going to magically succeed when
276 we get fired again. CFSocketInvalidate closes the fd for us and also
277 invalidates the run loop source for us which should cause it to
278 release the CFSocket (thus causing it to be deallocated) and remove
279 itself from the runloop which should release it and cause it to also
280 be deallocated. Of course, it's possible the RunLoop hangs onto
281 one or both of them by retaining/releasing them within its stack
282 frame. However, that shouldn't be depended on. Assume that s is
283 deallocated due to the following call.
284 */
285 CFSocketInvalidate(s);
286
287 // Now tell wx that the process has ended.
288 wxHandleProcessTermination(proc_data);
289}
290
291/*!
292 Implements the GUI-specific wxAddProcessCallback for both wxMac and
293 wxCocoa using the CFSocket/CFRunLoop API which is available to both.
294 Takes advantage of the fact that sockets on UNIX are just regular
295 file descriptors and thus even a non-socket file descriptor can
296 apparently be used with CFSocket so long as you only tell CFSocket
297 to do things with it that would be valid for a non-socket fd.
298 */
299int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
300{
301 static int s_last_tag = 0;
302 CFSocketContext context =
303 { 0
304 , static_cast<void*>(proc_data)
305 , NULL
306 , NULL
307 , NULL
308 };
309 CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
310 if(cfSocket == NULL)
311 {
312 wxLogError("Failed to create socket for end process detection");
313 return 0;
314 }
315 CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
316 if(runLoopSource == NULL)
317 {
318 wxLogError("Failed to create CFRunLoopSource from CFSocket for end process detection");
319 // closes the fd.. we can't really stop it, nor do we necessarily want to.
320 CFSocketInvalidate(cfSocket);
321 CFRelease(cfSocket);
322 return 0;
323 }
324 // Now that the run loop source has the socket retained and we no longer
325 // need to refer to it within this method, we can release it.
326 CFRelease(cfSocket);
327
328 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
329 // Now that the run loop has the source retained we can release it.
330 CFRelease(runLoopSource);
331
332 /*
333 Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
334 itself into our callback and that's enough to be able to
335 CFSocketInvalidate it which is all we need to do to get everything we
336 just created to be deallocated.
337 */
338 return ++s_last_tag;
339}
340
341/////////////////////////////////////////////////////////////////////////////
342
9e34f56b 343// NOTE: This doesn't really belong here but this was a handy file to
fc480dc1 344// put it in because it's already compiled for wxCocoa and wxMac GUI lib.
9e34f56b
VZ
345#if wxUSE_GUI
346
531814a7 347#if wxUSE_STDPATHS
fc480dc1
DE
348static wxStandardPathsCF gs_stdPaths;
349wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths()
350{
351 return gs_stdPaths;
352}
531814a7 353#endif
fc480dc1 354
9e34f56b
VZ
355#endif // wxUSE_GUI
356