]> git.saurik.com Git - wxWidgets.git/blame - src/unix/fswatcher_kqueue.cpp
wiring osx native notification during launch
[wxWidgets.git] / src / unix / fswatcher_kqueue.cpp
CommitLineData
6b8ef0b3
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/unix/fswatcher_kqueue.cpp
3// Purpose: kqueue-based wxFileSystemWatcher implementation
4// Author: Bartosz Bekier
5// Created: 2009-05-26
6// RCS-ID: $Id$
7// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif
17
18#if wxUSE_FSWATCHER
19
20#include "wx/fswatcher.h"
21
22#ifdef wxHAS_KQUEUE
23
24#include <sys/types.h>
25#include <sys/event.h>
5cd99866 26
6b8ef0b3 27#include "wx/dynarray.h"
5cd99866
VZ
28#include "wx/evtloop.h"
29#include "wx/evtloopsrc.h"
30
6b8ef0b3
VZ
31#include "wx/private/fswatcher.h"
32
5cd99866
VZ
33// ============================================================================
34// wxFSWSourceHandler helper class
35// ============================================================================
36
37class wxFSWatcherImplKqueue;
38
39/**
40 * Handler for handling i/o from inotify descriptor
41 */
42class wxFSWSourceHandler : public wxEventLoopSourceHandler
43{
44public:
45 wxFSWSourceHandler(wxFSWatcherImplKqueue* service) :
46 m_service(service)
47 { }
48
49 virtual void OnReadWaiting();
50 virtual void OnWriteWaiting();
51 virtual void OnExceptionWaiting();
52
53protected:
54 wxFSWatcherImplKqueue* m_service;
55};
56
6b8ef0b3
VZ
57// ============================================================================
58// wxFSWatcherImpl implementation & helper wxFSWSourceHandler implementation
59// ============================================================================
60
61/**
62 * Helper class encapsulating inotify mechanism
63 */
64class wxFSWatcherImplKqueue : public wxFSWatcherImpl
65{
66public:
67 wxFSWatcherImplKqueue(wxFileSystemWatcherBase* watcher) :
68 wxFSWatcherImpl(watcher),
6b8ef0b3
VZ
69 m_source(NULL),
70 m_kfd(-1)
71 {
72 m_handler = new wxFSWSourceHandler(this);
73 }
74
5cd99866 75 virtual ~wxFSWatcherImplKqueue()
6b8ef0b3
VZ
76 {
77 // we close kqueue only if initialized before
78 if (IsOk())
79 {
80 Close();
81 }
82
83 delete m_handler;
84 }
85
86 bool Init()
87 {
88 wxCHECK_MSG( !IsOk(), false,
89 "Kqueue appears to be already initialized" );
6b8ef0b3 90
5cd99866
VZ
91 wxEventLoopBase *loop = wxEventLoopBase::GetActive();
92 wxCHECK_MSG( loop, false, "File system watcher needs an active loop" );
6b8ef0b3
VZ
93
94 // create kqueue
95 m_kfd = kqueue();
96 if (m_kfd == -1)
97 {
98 wxLogSysError(_("Unable to create kqueue instance"));
99 return false;
100 }
101
102 // create source
5cd99866 103 m_source = loop->AddSourceForFD(m_kfd, m_handler, wxEVENT_SOURCE_INPUT);
6b8ef0b3 104
5cd99866 105 return m_source != NULL;
6b8ef0b3
VZ
106 }
107
5cd99866 108 void Close()
6b8ef0b3 109 {
5cd99866 110 wxCHECK_RET( IsOk(),
6b8ef0b3 111 "Kqueue not initialized or invalid kqueue descriptor" );
6b8ef0b3 112
5cd99866 113 if ( close(m_kfd) != 0 )
6b8ef0b3 114 {
5cd99866 115 wxLogSysError(_("Error closing kqueue instance"));
6b8ef0b3 116 }
6b8ef0b3 117
5276b0a5 118 wxDELETE(m_source);
6b8ef0b3
VZ
119 }
120
121 virtual bool DoAdd(wxSharedPtr<wxFSWatchEntryKq> watch)
122 {
123 wxCHECK_MSG( IsOk(), false,
124 "Kqueue not initialized or invalid kqueue descriptor" );
125
126 struct kevent event;
127 int action = EV_ADD | EV_ENABLE | EV_CLEAR | EV_ERROR;
128 int flags = Watcher2NativeFlags(watch->GetFlags());
129 EV_SET( &event, watch->GetFileDescriptor(), EVFILT_VNODE, action,
130 flags, 0, watch.get() );
131
132 // TODO more error conditions according to man
133 // TODO best deal with the error here
134 int ret = kevent(m_kfd, &event, 1, NULL, 0, NULL);
135 if (ret == -1)
136 {
137 wxLogSysError(_("Unable to add kqueue watch"));
138 return false;
139 }
140
141 return true;
142 }
143
144 virtual bool DoRemove(wxSharedPtr<wxFSWatchEntryKq> watch)
145 {
146 wxCHECK_MSG( IsOk(), false,
147 "Kqueue not initialized or invalid kqueue descriptor" );
148
149 // TODO more error conditions according to man
150 // XXX closing file descriptor removes the watch. The logic resides in
151 // the watch which is not nice, but effective and simple
70a5b905 152 if ( !watch->Close() )
6b8ef0b3
VZ
153 {
154 wxLogSysError(_("Unable to remove kqueue watch"));
155 return false;
156 }
157
158 return true;
159 }
160
161 virtual bool RemoveAll()
162 {
163 wxFSWatchEntries::iterator it = m_watches.begin();
164 for ( ; it != m_watches.end(); ++it )
165 {
166 (void) DoRemove(it->second);
167 }
168 m_watches.clear();
169 return true;
170 }
171
172 // return true if there was no error, false on error
173 bool ReadEvents()
174 {
175 wxCHECK_MSG( IsOk(), false,
176 "Kqueue not initialized or invalid kqueue descriptor" );
177
178 // read events
179 do
180 {
181 struct kevent event;
182 struct timespec timeout = {0, 0};
183 int ret = kevent(m_kfd, NULL, 0, &event, 1, &timeout);
184 if (ret == -1)
185 {
186 wxLogSysError(_("Unable to get events from kqueue"));
187 return false;
188 }
189 else if (ret == 0)
190 {
191 return true;
192 }
193
194 // we have event, so process it
195 ProcessNativeEvent(event);
196 }
197 while (true);
198
199 // when ret>0 we still have events, when ret<=0 we return
200 wxFAIL_MSG( "Never reached" );
201 return true;
202 }
203
5cd99866 204 bool IsOk() const
6b8ef0b3 205 {
5cd99866 206 return m_source != NULL;
6b8ef0b3
VZ
207 }
208
6b8ef0b3 209protected:
6b8ef0b3
VZ
210 // returns all new dirs/files present in the immediate level of the dir
211 // pointed by watch.GetPath(). "new" means created between the last time
212 // the state of watch was computed and now
5cd99866
VZ
213 void FindChanges(wxFSWatchEntryKq& watch,
214 wxArrayString& changedFiles,
215 wxArrayInt& changedFlags)
6b8ef0b3
VZ
216 {
217 wxFSWatchEntryKq::wxDirState old = watch.GetLastState();
218 watch.RefreshState();
219 wxFSWatchEntryKq::wxDirState curr = watch.GetLastState();
220
221 // iterate over old/curr file lists and compute changes
222 wxArrayString::iterator oit = old.files.begin();
223 wxArrayString::iterator cit = curr.files.begin();
224 for ( ; oit != old.files.end() && cit != curr.files.end(); )
225 {
226 if ( *cit == *oit )
227 {
228 ++cit;
229 ++oit;
230 }
231 else if ( *cit <= *oit )
232 {
233 changedFiles.push_back(*cit);
234 changedFlags.push_back(wxFSW_EVENT_CREATE);
235 ++cit;
236 }
237 else // ( *cit > *oit )
238 {
239 changedFiles.push_back(*oit);
240 changedFlags.push_back(wxFSW_EVENT_DELETE);
241 ++oit;
242 }
243 }
244
245 // border conditions
246 if ( oit == old.files.end() )
247 {
248 for ( ; cit != curr.files.end(); ++cit )
249 {
250 changedFiles.push_back( *cit );
251 changedFlags.push_back(wxFSW_EVENT_CREATE);
252 }
253 }
254 else if ( cit == curr.files.end() )
255 {
256 for ( ; oit != old.files.end(); ++oit )
257 {
258 changedFiles.push_back( *oit );
259 changedFlags.push_back(wxFSW_EVENT_DELETE);
260 }
261 }
262
263 wxASSERT( changedFiles.size() == changedFlags.size() );
264
265#if 0
266 wxLogTrace(wxTRACE_FSWATCHER, "Changed files:");
267 wxArrayString::iterator it = changedFiles.begin();
268 wxArrayInt::iterator it2 = changedFlags.begin();
269 for ( ; it != changedFiles.end(); ++it, ++it2)
270 {
271 wxString action = (*it2 == wxFSW_EVENT_CREATE) ?
272 "created" : "deleted";
273 wxLogTrace(wxTRACE_FSWATCHER, wxString::Format("File: '%s' %s",
274 *it, action));
275 }
276#endif
277 }
278
279 void ProcessNativeEvent(const struct kevent& e)
280 {
281 wxASSERT_MSG(e.udata, "Null user data associated with kevent!");
282
283 wxLogTrace(wxTRACE_FSWATCHER, "Event: ident=%d, filter=%d, flags=%u, "
284 "fflags=%u, data=%d, user_data=%p",
285 e.ident, e.filter, e.flags, e.fflags, e.data, e.udata);
286
287 // for ease of use
288 wxFSWatchEntryKq& w = *(static_cast<wxFSWatchEntry*>(e.udata));
289 int nflags = e.fflags;
290
291 // clear ignored flags
292 nflags &= ~NOTE_REVOKE;
293
294 // TODO ignore events we didn't ask for + refactor this cascade ifs
295 // check for events
296 while ( nflags )
297 {
298 // when monitoring dir, this means create/delete
f3b1d0fc
VZ
299 const wxString basepath = w.GetPath();
300 if ( nflags & NOTE_WRITE && wxDirExists(basepath) )
6b8ef0b3
VZ
301 {
302 // NOTE_LINK is set when the dir was created, but we
303 // don't care - we look for new names in directory
304 // regardless of type. Also, clear all this, because
305 // it cannot mean more by itself
306 nflags &= ~(NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK);
307
308 wxArrayString changedFiles;
309 wxArrayInt changedFlags;
310 FindChanges(w, changedFiles, changedFlags);
f3b1d0fc 311
6b8ef0b3
VZ
312 wxArrayString::iterator it = changedFiles.begin();
313 wxArrayInt::iterator changeType = changedFlags.begin();
314 for ( ; it != changedFiles.end(); ++it, ++changeType )
315 {
d66ddd70
VZ
316 const wxString fullpath = w.GetPath() +
317 wxFileName::GetPathSeparator() +
318 *it;
319 const wxFileName path(wxDirExists(fullpath)
320 ? wxFileName::DirName(fullpath)
321 : wxFileName::FileName(fullpath));
6b8ef0b3
VZ
322
323 wxFileSystemWatcherEvent event(*changeType, path, path);
324 SendEvent(event);
325 }
326 }
327 else if ( nflags & NOTE_RENAME )
328 {
329 // CHECK it'd be only possible to detect name if we had
330 // parent files listing which we could confront with now and
331 // still we couldn't be sure we have the right name...
332 nflags &= ~NOTE_RENAME;
333 wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME,
f3b1d0fc 334 basepath, wxFileName());
6b8ef0b3
VZ
335 SendEvent(event);
336 }
337 else if ( nflags & NOTE_WRITE || nflags & NOTE_EXTEND )
338 {
339 nflags &= ~(NOTE_WRITE | NOTE_EXTEND);
340 wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY,
f3b1d0fc 341 basepath, basepath);
6b8ef0b3
VZ
342 SendEvent(event);
343 }
344 else if ( nflags & NOTE_DELETE )
345 {
346 nflags &= ~(NOTE_DELETE);
347 wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE,
f3b1d0fc 348 basepath, basepath);
6b8ef0b3
VZ
349 SendEvent(event);
350 }
351 else if ( nflags & NOTE_ATTRIB )
352 {
353 nflags &= ~(NOTE_ATTRIB);
354 wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS,
f3b1d0fc 355 basepath, basepath);
6b8ef0b3
VZ
356 SendEvent(event);
357 }
358
359 // clear any flags that won't mean anything by themselves
360 nflags &= ~(NOTE_LINK);
361 }
362 }
363
364 void SendEvent(wxFileSystemWatcherEvent& evt)
365 {
366 m_watcher->GetOwner()->ProcessEvent(evt);
367 }
368
369 static int Watcher2NativeFlags(int WXUNUSED(flags))
370 {
371 // TODO: it would be better to only subscribe to what we need
372 return NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND |
373 NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME |
374 NOTE_REVOKE;
375 }
376
377 wxFSWSourceHandler* m_handler; // handler for kqueue event source
5cd99866
VZ
378 wxEventLoopSource* m_source; // our event loop source
379
380 // descriptor created by kqueue()
6b8ef0b3
VZ
381 int m_kfd;
382};
383
384
385// once we get signaled to read, actuall event reading occurs
386void wxFSWSourceHandler::OnReadWaiting()
387{
388 wxLogTrace(wxTRACE_FSWATCHER, "--- OnReadWaiting ---");
389 m_service->ReadEvents();
390}
391
392void wxFSWSourceHandler::OnWriteWaiting()
393{
394 wxFAIL_MSG("We never write to kqueue descriptor.");
395}
396
397void wxFSWSourceHandler::OnExceptionWaiting()
398{
399 wxFAIL_MSG("We never receive exceptions on kqueue descriptor.");
400}
401
402
403// ============================================================================
404// wxKqueueFileSystemWatcher implementation
405// ============================================================================
406
407wxKqueueFileSystemWatcher::wxKqueueFileSystemWatcher()
408 : wxFileSystemWatcherBase()
409{
410 Init();
411}
412
413wxKqueueFileSystemWatcher::wxKqueueFileSystemWatcher(const wxFileName& path,
414 int events)
415 : wxFileSystemWatcherBase()
416{
417 if (!Init())
418 {
5276b0a5 419 wxDELETE(m_service);
6b8ef0b3
VZ
420 return;
421 }
422
423 Add(path, events);
424}
425
426wxKqueueFileSystemWatcher::~wxKqueueFileSystemWatcher()
427{
428}
429
430bool wxKqueueFileSystemWatcher::Init()
431{
432 m_service = new wxFSWatcherImplKqueue(this);
433 return m_service->Init();
434}
435
436#endif // wxHAS_KQUEUE
437
438#endif // wxUSE_FSWATCHER