]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/utilsexc_cf.cpp
Add support for launching APPL bundles with wxExecute
[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"
5d553c56 21
62705a27
RN
22#ifdef __WXCOCOA__
23#include <CoreFoundation/CoreFoundation.h>
24#include <ApplicationServices/ApplicationServices.h>
25#else
26#include "wx/mac/private.h"
27#include "LaunchServices.h"
28#endif
29
30#include "wx/uri.h"
31#include "wx/mac/corefoundation/cfstring.h"
32
33long wxMacExecute(wxChar **argv,
34 int flags,
35 wxProcess *process)
36{
37 CFIndex cfiCount = 0;
38 //get count
39 for(wxChar** argvcopy = argv; *argvcopy != NULL ; ++argvcopy)
40 {
41 ++cfiCount;
42 }
43
44 if(cfiCount == 0) //no file to launch?
45 {
46 wxLogDebug(wxT("wxMacExecute No file to launch!"));
47 return -1;
48 }
49
50 CFURLRef cfurlApp = CFURLCreateWithString(
51 kCFAllocatorDefault,
52 wxMacCFStringHolder(*argv++, wxLocale::GetSystemEncoding()),
53 NULL);
54 wxASSERT(cfurlApp);
55
56 CFBundleRef cfbApp = CFBundleCreate(kCFAllocatorDefault, cfurlApp);
57 if(!cfbApp)
58 {
59 wxLogDebug(wxT("wxMacExecute Bad bundle"));
60 CFRelease(cfurlApp);
61 return -1;
62 }
63
64
65 UInt32 dwBundleType, dwBundleCreator;
66 CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator);
67
68 //Only call wxMacExecute for .app bundles - others could be actual unix programs
69 if(dwBundleType != 'APPL')
70 {
71 CFRelease(cfurlApp);
72 return -1;
73 }
74
75 //
76 // We have a good bundle - so let's launch it!
77 //
78
79 CFMutableArrayRef cfaFiles = CFArrayCreateMutable(kCFAllocatorDefault, cfiCount - 1, NULL);
80
81 wxASSERT(cfaFiles);
82
83 if(--cfiCount)
84 {
85 for( ; *argv != NULL ; ++argv)
86 {
87// wxLogDebug(*argv);
88 wxString sCurrentFile;
89
90 if(wxURI(*argv).IsReference())
91 sCurrentFile = wxString(wxT("file://")) + *argv;
92 else
93 sCurrentFile = *argv;
94
95 CFURLRef cfurlCurrentFile = CFURLCreateWithString(
96 kCFAllocatorDefault,
97 wxMacCFStringHolder(sCurrentFile, wxLocale::GetSystemEncoding()),
98 NULL);
99 wxASSERT(cfurlCurrentFile);
100
101 CFArrayAppendValue(
102 cfaFiles,
103 cfurlCurrentFile
104 );
105 }
106 }
107
108 LSLaunchURLSpec launchspec;
109 launchspec.appURL = cfurlApp;
110 launchspec.itemURLs = cfaFiles;
111 launchspec.passThruParams = NULL; //AEDesc*
112 launchspec.launchFlags = kLSLaunchDefaults | kLSLaunchDontSwitch; //TODO: Possibly be smarter with flags
113 launchspec.asyncRefCon = NULL;
114
115 OSStatus status = LSOpenFromURLSpec(&launchspec,
116 NULL); //2nd is CFURLRef* really launched
117
118 //cleanup
119 CFRelease(cfurlApp);
120 CFRelease(cfaFiles);
121
122 //check for error
123 if(status != noErr)
124 {
125 wxLogDebug(wxString::Format(wxT("wxMacExecute ERROR: %d")), (int)status);
126 return -1;
127 }
128 return 0; //success
129}
130
131
5d553c56
DE
132#include <CoreFoundation/CFMachPort.h>
133#include <sys/wait.h>
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 {
146 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
147 return;
148 }
149 if((rc != -1) && WIFEXITED(status))
150 proc_data->exitcode = WEXITSTATUS(status);
151 else
152 proc_data->exitcode = -1;
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 }
215
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
fc480dc1
DE
224// NOTE: This doens't really belong here but this was a handy file to
225// put it in because it's already compiled for wxCocoa and wxMac GUI lib.
226static wxStandardPathsCF gs_stdPaths;
227wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths()
228{
229 return gs_stdPaths;
230}
231