1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/fswatcher_kqueue.cpp
3 // Purpose: kqueue-based wxFileSystemWatcher implementation
4 // Author: Bartosz Bekier
7 // Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
20 #include "wx/fswatcher.h"
24 #include <sys/types.h>
25 #include <sys/event.h>
27 #include "wx/dynarray.h"
28 #include "wx/evtloop.h"
29 #include "wx/evtloopsrc.h"
31 #include "wx/private/fswatcher.h"
33 // ============================================================================
34 // wxFSWSourceHandler helper class
35 // ============================================================================
37 class wxFSWatcherImplKqueue
;
40 * Handler for handling i/o from inotify descriptor
42 class wxFSWSourceHandler
: public wxEventLoopSourceHandler
45 wxFSWSourceHandler(wxFSWatcherImplKqueue
* service
) :
49 virtual void OnReadWaiting();
50 virtual void OnWriteWaiting();
51 virtual void OnExceptionWaiting();
54 wxFSWatcherImplKqueue
* m_service
;
57 // ============================================================================
58 // wxFSWatcherImpl implementation & helper wxFSWSourceHandler implementation
59 // ============================================================================
62 * Helper class encapsulating inotify mechanism
64 class wxFSWatcherImplKqueue
: public wxFSWatcherImpl
67 wxFSWatcherImplKqueue(wxFileSystemWatcherBase
* watcher
) :
68 wxFSWatcherImpl(watcher
),
72 m_handler
= new wxFSWSourceHandler(this);
75 virtual ~wxFSWatcherImplKqueue()
77 // we close kqueue only if initialized before
88 wxCHECK_MSG( !IsOk(), false,
89 "Kqueue appears to be already initialized" );
91 wxEventLoopBase
*loop
= wxEventLoopBase::GetActive();
92 wxCHECK_MSG( loop
, false, "File system watcher needs an active loop" );
98 wxLogSysError(_("Unable to create kqueue instance"));
103 m_source
= loop
->AddSourceForFD(m_kfd
, m_handler
, wxEVENT_SOURCE_INPUT
);
105 return m_source
!= NULL
;
111 "Kqueue not initialized or invalid kqueue descriptor" );
113 if ( close(m_kfd
) != 0 )
115 wxLogSysError(_("Error closing kqueue instance"));
122 virtual bool DoAdd(wxSharedPtr
<wxFSWatchEntryKq
> watch
)
124 wxCHECK_MSG( IsOk(), false,
125 "Kqueue not initialized or invalid kqueue descriptor" );
128 int action
= EV_ADD
| EV_ENABLE
| EV_CLEAR
| EV_ERROR
;
129 int flags
= Watcher2NativeFlags(watch
->GetFlags());
130 EV_SET( &event
, watch
->GetFileDescriptor(), EVFILT_VNODE
, action
,
131 flags
, 0, watch
.get() );
133 // TODO more error conditions according to man
134 // TODO best deal with the error here
135 int ret
= kevent(m_kfd
, &event
, 1, NULL
, 0, NULL
);
138 wxLogSysError(_("Unable to add kqueue watch"));
145 virtual bool DoRemove(wxSharedPtr
<wxFSWatchEntryKq
> watch
)
147 wxCHECK_MSG( IsOk(), false,
148 "Kqueue not initialized or invalid kqueue descriptor" );
150 // TODO more error conditions according to man
151 // XXX closing file descriptor removes the watch. The logic resides in
152 // the watch which is not nice, but effective and simple
153 bool ret
= watch
->Close();
156 wxLogSysError(_("Unable to remove kqueue watch"));
163 virtual bool RemoveAll()
165 wxFSWatchEntries::iterator it
= m_watches
.begin();
166 for ( ; it
!= m_watches
.end(); ++it
)
168 (void) DoRemove(it
->second
);
174 // return true if there was no error, false on error
177 wxCHECK_MSG( IsOk(), false,
178 "Kqueue not initialized or invalid kqueue descriptor" );
184 struct timespec timeout
= {0, 0};
185 int ret
= kevent(m_kfd
, NULL
, 0, &event
, 1, &timeout
);
188 wxLogSysError(_("Unable to get events from kqueue"));
196 // we have event, so process it
197 ProcessNativeEvent(event
);
201 // when ret>0 we still have events, when ret<=0 we return
202 wxFAIL_MSG( "Never reached" );
208 return m_source
!= NULL
;
212 // returns all new dirs/files present in the immediate level of the dir
213 // pointed by watch.GetPath(). "new" means created between the last time
214 // the state of watch was computed and now
215 void FindChanges(wxFSWatchEntryKq
& watch
,
216 wxArrayString
& changedFiles
,
217 wxArrayInt
& changedFlags
)
219 wxFSWatchEntryKq::wxDirState old
= watch
.GetLastState();
220 watch
.RefreshState();
221 wxFSWatchEntryKq::wxDirState curr
= watch
.GetLastState();
223 // iterate over old/curr file lists and compute changes
224 wxArrayString::iterator oit
= old
.files
.begin();
225 wxArrayString::iterator cit
= curr
.files
.begin();
226 for ( ; oit
!= old
.files
.end() && cit
!= curr
.files
.end(); )
233 else if ( *cit
<= *oit
)
235 changedFiles
.push_back(*cit
);
236 changedFlags
.push_back(wxFSW_EVENT_CREATE
);
239 else // ( *cit > *oit )
241 changedFiles
.push_back(*oit
);
242 changedFlags
.push_back(wxFSW_EVENT_DELETE
);
248 if ( oit
== old
.files
.end() )
250 for ( ; cit
!= curr
.files
.end(); ++cit
)
252 changedFiles
.push_back( *cit
);
253 changedFlags
.push_back(wxFSW_EVENT_CREATE
);
256 else if ( cit
== curr
.files
.end() )
258 for ( ; oit
!= old
.files
.end(); ++oit
)
260 changedFiles
.push_back( *oit
);
261 changedFlags
.push_back(wxFSW_EVENT_DELETE
);
265 wxASSERT( changedFiles
.size() == changedFlags
.size() );
268 wxLogTrace(wxTRACE_FSWATCHER
, "Changed files:");
269 wxArrayString::iterator it
= changedFiles
.begin();
270 wxArrayInt::iterator it2
= changedFlags
.begin();
271 for ( ; it
!= changedFiles
.end(); ++it
, ++it2
)
273 wxString action
= (*it2
== wxFSW_EVENT_CREATE
) ?
274 "created" : "deleted";
275 wxLogTrace(wxTRACE_FSWATCHER
, wxString::Format("File: '%s' %s",
281 void ProcessNativeEvent(const struct kevent
& e
)
283 wxASSERT_MSG(e
.udata
, "Null user data associated with kevent!");
285 wxLogTrace(wxTRACE_FSWATCHER
, "Event: ident=%d, filter=%d, flags=%u, "
286 "fflags=%u, data=%d, user_data=%p",
287 e
.ident
, e
.filter
, e
.flags
, e
.fflags
, e
.data
, e
.udata
);
290 wxFSWatchEntryKq
& w
= *(static_cast<wxFSWatchEntry
*>(e
.udata
));
291 int nflags
= e
.fflags
;
293 // clear ignored flags
294 nflags
&= ~NOTE_REVOKE
;
296 // TODO ignore events we didn't ask for + refactor this cascade ifs
300 // when monitoring dir, this means create/delete
301 if ( nflags
& NOTE_WRITE
&& wxDirExists(w
.GetPath()) )
303 // NOTE_LINK is set when the dir was created, but we
304 // don't care - we look for new names in directory
305 // regardless of type. Also, clear all this, because
306 // it cannot mean more by itself
307 nflags
&= ~(NOTE_WRITE
| NOTE_ATTRIB
| NOTE_LINK
);
309 wxArrayString changedFiles
;
310 wxArrayInt changedFlags
;
311 FindChanges(w
, changedFiles
, changedFlags
);
312 wxArrayString::iterator it
= changedFiles
.begin();
313 wxArrayInt::iterator changeType
= changedFlags
.begin();
314 for ( ; it
!= changedFiles
.end(); ++it
, ++changeType
)
317 if ( wxDirExists(*it
) )
319 path
= wxFileName::DirName(w
.GetPath() + *it
);
323 path
= wxFileName::FileName(w
.GetPath() + *it
);
326 wxFileSystemWatcherEvent
event(*changeType
, path
, path
);
330 else if ( nflags
& NOTE_RENAME
)
332 // CHECK it'd be only possible to detect name if we had
333 // parent files listing which we could confront with now and
334 // still we couldn't be sure we have the right name...
335 nflags
&= ~NOTE_RENAME
;
336 wxFileSystemWatcherEvent
event(wxFSW_EVENT_RENAME
,
337 w
.GetPath(), wxFileName());
340 else if ( nflags
& NOTE_WRITE
|| nflags
& NOTE_EXTEND
)
342 nflags
&= ~(NOTE_WRITE
| NOTE_EXTEND
);
343 wxFileSystemWatcherEvent
event(wxFSW_EVENT_MODIFY
,
344 w
.GetPath(), w
.GetPath());
347 else if ( nflags
& NOTE_DELETE
)
349 nflags
&= ~(NOTE_DELETE
);
350 wxFileSystemWatcherEvent
event(wxFSW_EVENT_DELETE
,
351 w
.GetPath(), w
.GetPath());
354 else if ( nflags
& NOTE_ATTRIB
)
356 nflags
&= ~(NOTE_ATTRIB
);
357 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
,
358 w
.GetPath(), w
.GetPath());
362 // clear any flags that won't mean anything by themselves
363 nflags
&= ~(NOTE_LINK
);
367 void SendEvent(wxFileSystemWatcherEvent
& evt
)
369 m_watcher
->GetOwner()->ProcessEvent(evt
);
372 static int Watcher2NativeFlags(int WXUNUSED(flags
))
374 // TODO: it would be better to only subscribe to what we need
375 return NOTE_DELETE
| NOTE_WRITE
| NOTE_EXTEND
|
376 NOTE_ATTRIB
| NOTE_LINK
| NOTE_RENAME
|
380 wxFSWSourceHandler
* m_handler
; // handler for kqueue event source
381 wxEventLoopSource
* m_source
; // our event loop source
383 // descriptor created by kqueue()
388 // once we get signaled to read, actuall event reading occurs
389 void wxFSWSourceHandler::OnReadWaiting()
391 wxLogTrace(wxTRACE_FSWATCHER
, "--- OnReadWaiting ---");
392 m_service
->ReadEvents();
395 void wxFSWSourceHandler::OnWriteWaiting()
397 wxFAIL_MSG("We never write to kqueue descriptor.");
400 void wxFSWSourceHandler::OnExceptionWaiting()
402 wxFAIL_MSG("We never receive exceptions on kqueue descriptor.");
406 // ============================================================================
407 // wxKqueueFileSystemWatcher implementation
408 // ============================================================================
410 wxKqueueFileSystemWatcher::wxKqueueFileSystemWatcher()
411 : wxFileSystemWatcherBase()
416 wxKqueueFileSystemWatcher::wxKqueueFileSystemWatcher(const wxFileName
& path
,
418 : wxFileSystemWatcherBase()
433 wxKqueueFileSystemWatcher::~wxKqueueFileSystemWatcher()
437 bool wxKqueueFileSystemWatcher::Init()
439 m_service
= new wxFSWatcherImplKqueue(this);
440 return m_service
->Init();
443 #endif // wxHAS_KQUEUE
445 #endif // wxUSE_FSWATCHER