]> git.saurik.com Git - wxWidgets.git/blob - src/osx/core/utilsexc_cf.cpp
Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource.
[wxWidgets.git] / src / osx / core / utilsexc_cf.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/core/utilsexc_cf.cpp
3 // Purpose: Execution-related utilities for Darwin
4 // Author: David Elliott, Ryan Norton (wxMacExecute)
5 // Modified by: Stefan Csomor (added necessary wxT for unicode builds)
6 // Created: 2004-11-04
7 // RCS-ID: $Id$
8 // Copyright: (c) David Elliott, Ryan Norton
9 // Licence: wxWindows licence
10 // Notes: This code comes from src/osx/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"
19 #include "wx/stdpaths.h"
20 #include "wx/app.h"
21 #include "wx/apptrait.h"
22 #include "wx/thread.h"
23 #include "wx/process.h"
24
25 #include "wx/evtloop.h"
26 #include "wx/evtloopsrc.h"
27 #include "wx/private/eventloopsourcesmanager.h"
28
29 #include <sys/wait.h>
30
31 #include <CoreFoundation/CFSocket.h>
32
33 /*!
34 Called due to source signal detected by the CFRunLoop.
35 This is nearly identical to the wxGTK equivalent.
36 */
37 extern "C" void WXCF_EndProcessDetector(CFSocketRef s,
38 CFSocketCallBackType WXUNUSED(callbackType),
39 CFDataRef WXUNUSED(address),
40 void const *WXUNUSED(data),
41 void *info)
42 {
43 /*
44 Either our pipe was closed or the process ended successfully. Either way,
45 we're done. It's not if waitpid is going to magically succeed when
46 we get fired again. CFSocketInvalidate closes the fd for us and also
47 invalidates the run loop source for us which should cause it to
48 release the CFSocket (thus causing it to be deallocated) and remove
49 itself from the runloop which should release it and cause it to also
50 be deallocated. Of course, it's possible the RunLoop hangs onto
51 one or both of them by retaining/releasing them within its stack
52 frame. However, that shouldn't be depended on. Assume that s is
53 deallocated due to the following call.
54 */
55 CFSocketInvalidate(s);
56
57 // Now tell wx that the process has ended.
58 wxHandleProcessTermination(static_cast<wxEndProcessData *>(info));
59 }
60
61 /*!
62 Implements the GUI-specific AddProcessCallback() for both wxMac and
63 wxCocoa using the CFSocket/CFRunLoop API which is available to both.
64 Takes advantage of the fact that sockets on UNIX are just regular
65 file descriptors and thus even a non-socket file descriptor can
66 apparently be used with CFSocket so long as you only tell CFSocket
67 to do things with it that would be valid for a non-socket fd.
68 */
69 int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
70 {
71 static int s_last_tag = 0;
72 CFSocketContext context =
73 { 0
74 , static_cast<void*>(proc_data)
75 , NULL
76 , NULL
77 , NULL
78 };
79 CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
80 if(cfSocket == NULL)
81 {
82 wxLogError(wxT("Failed to create socket for end process detection"));
83 return 0;
84 }
85 CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
86 if(runLoopSource == NULL)
87 {
88 wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection"));
89 // closes the fd.. we can't really stop it, nor do we necessarily want to.
90 CFSocketInvalidate(cfSocket);
91 CFRelease(cfSocket);
92 return 0;
93 }
94 // Now that the run loop source has the socket retained and we no longer
95 // need to refer to it within this method, we can release it.
96 CFRelease(cfSocket);
97
98 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
99 // Now that the run loop has the source retained we can release it.
100 CFRelease(runLoopSource);
101
102 /*
103 Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
104 itself into our callback and that's enough to be able to
105 CFSocketInvalidate it which is all we need to do to get everything we
106 just created to be deallocated.
107 */
108 return ++s_last_tag;
109 }
110
111 #if wxUSE_EVENTLOOP_SOURCE
112
113 namespace
114 {
115
116 extern "C"
117 void
118 wx_socket_callback(CFSocketRef WXUNUSED(s),
119 CFSocketCallBackType callbackType,
120 CFDataRef WXUNUSED(address),
121 void const *WXUNUSED(data),
122 void *ctxData)
123 {
124 wxLogTrace(wxTRACE_EVT_SOURCE,
125 "CFSocket callback, type=%d", callbackType);
126
127 wxCFEventLoopSource * const
128 source = static_cast<wxCFEventLoopSource *>(ctxData);
129
130 wxEventLoopSourceHandler * const
131 handler = source->GetHandler();
132
133 switch ( callbackType )
134 {
135 case kCFSocketReadCallBack:
136 handler->OnReadWaiting();
137 break;
138
139 case kCFSocketWriteCallBack:
140 handler->OnWriteWaiting();
141 break;
142
143 default:
144 wxFAIL_MSG( "Unexpected callback type." );
145 }
146 }
147
148 } // anonymous namespace
149
150 class wxCFEventLoopSourcesManager : public wxEventLoopSourcesManagerBase
151 {
152 public:
153 wxEventLoopSource *
154 AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags)
155 {
156 wxCHECK_MSG( fd != -1, NULL, "can't monitor invalid fd" );
157
158 wxScopedPtr<wxCFEventLoopSource>
159 source(new wxCFEventLoopSource(handler, flags));
160
161 CFSocketContext context = { 0, source.get(), NULL, NULL, NULL };
162
163 int callbackTypes = 0;
164 if ( flags & wxEVENT_SOURCE_INPUT )
165 callbackTypes |= kCFSocketReadCallBack;
166 if ( flags & wxEVENT_SOURCE_OUTPUT )
167 callbackTypes |= kCFSocketWriteCallBack;
168
169 wxCFRef<CFSocketRef>
170 cfSocket(CFSocketCreateWithNative
171 (
172 kCFAllocatorDefault,
173 fd,
174 callbackTypes,
175 &wx_socket_callback,
176 &context
177 ));
178
179 if ( !cfSocket )
180 {
181 wxLogError(wxS("Failed to create event loop source socket."));
182 return NULL;
183 }
184
185 // Adjust the socket options to suit our needs:
186 CFOptionFlags sockopt = CFSocketGetSocketFlags(cfSocket);
187
188 // First, by default, write callback is not called repeatedly when data
189 // can be written to the socket but we need this behaviour so request
190 // it explicitly.
191 if ( flags & wxEVENT_SOURCE_OUTPUT )
192 sockopt |= kCFSocketAutomaticallyReenableWriteCallBack;
193
194 // Second, we use the socket to monitor the FD but it doesn't own it,
195 // so prevent the FD from being closed when the socket is invalidated.
196 sockopt &= ~kCFSocketCloseOnInvalidate;
197
198 CFSocketSetSocketFlags(cfSocket, sockopt);
199
200 wxCFRef<CFRunLoopSourceRef>
201 runLoopSource(CFSocketCreateRunLoopSource
202 (
203 kCFAllocatorDefault,
204 cfSocket,
205 0 // Lowest index means highest priority
206 ));
207 if ( !runLoopSource )
208 {
209 wxLogError(wxS("Failed to create low level event loop source."));
210 CFSocketInvalidate(cfSocket);
211 return NULL;
212 }
213
214 // Save the socket so that we can remove it later if asked to.
215 source->InitSourceSocket(cfSocket.release());
216
217 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
218
219 return source.release();
220 }
221 };
222
223 wxEventLoopSourcesManagerBase* wxGUIAppTraits::GetEventLoopSourcesManager()
224 {
225 static wxCFEventLoopSourcesManager s_eventLoopSourcesManager;
226
227 return &s_eventLoopSourcesManager;
228 }
229
230 #endif // wxUSE_EVENTLOOP_SOURCE
231
232 /////////////////////////////////////////////////////////////////////////////
233
234 // NOTE: This doesn't really belong here but this was a handy file to
235 // put it in because it's already compiled for wxCocoa and wxMac GUI lib.
236 #if wxUSE_STDPATHS
237 static wxStandardPathsCF gs_stdPaths;
238 wxStandardPaths& wxGUIAppTraits::GetStandardPaths()
239 {
240 return gs_stdPaths;
241 }
242 #endif
243
244 #if wxUSE_SOCKETS
245
246 // we need to implement this method in a file of the core library as it should
247 // only be used for the GUI applications but we can't use socket stuff from it
248 // directly as this would create unwanted dependencies of core on net library
249 //
250 // so we have this global pointer which is set from sockosx.cpp when it is
251 // linked in and we simply return it from here
252 extern WXDLLIMPEXP_BASE wxSocketManager *wxOSXSocketManagerCF;
253 wxSocketManager *wxGUIAppTraits::GetSocketManager()
254 {
255 return wxOSXSocketManagerCF ? wxOSXSocketManagerCF
256 : wxGUIAppTraitsBase::GetSocketManager();
257 }
258
259 #endif // wxUSE_SOCKETS