]> git.saurik.com Git - wxWidgets.git/blame - src/unix/fswatcher_inotify.cpp
leave i386 compiler to default
[wxWidgets.git] / src / unix / fswatcher_inotify.cpp
CommitLineData
6b8ef0b3
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/unix/fswatcher_inotify.cpp
3// Purpose: inotify-based wxFileSystemWatcher implementation
4// Author: Bartosz Bekier
5// Created: 2009-05-26
6b8ef0b3
VZ
6// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14 #pragma hdrstop
15#endif
16
17#if wxUSE_FSWATCHER
18
19#include "wx/fswatcher.h"
20
21#ifdef wxHAS_INOTIFY
22
23#include <sys/inotify.h>
24#include <unistd.h>
25#include "wx/private/fswatcher.h"
26
27// ============================================================================
28// wxFSWatcherImpl implementation & helper wxFSWSourceHandler implementation
29// ============================================================================
30
31// inotify watch descriptor => wxFSWatchEntry* map
32WX_DECLARE_HASH_MAP(int, wxFSWatchEntry*, wxIntegerHash, wxIntegerEqual,
33 wxFSWatchEntryDescriptors);
34
35// inotify event cookie => inotify_event* map
36WX_DECLARE_HASH_MAP(int, inotify_event*, wxIntegerHash, wxIntegerEqual,
37 wxInotifyCookies);
38
39/**
40 * Helper class encapsulating inotify mechanism
41 */
42class wxFSWatcherImplUnix : public wxFSWatcherImpl
43{
44public:
45 wxFSWatcherImplUnix(wxFileSystemWatcherBase* watcher) :
46 wxFSWatcherImpl(watcher),
5cd99866
VZ
47 m_source(NULL),
48 m_ifd(-1)
6b8ef0b3
VZ
49 {
50 m_handler = new wxFSWSourceHandler(this);
51 }
52
53 ~wxFSWatcherImplUnix()
54 {
55 // we close inotify only if initialized before
56 if (IsOk())
57 {
58 Close();
59 }
60
61 delete m_handler;
62 }
63
64 bool Init()
65 {
66 wxCHECK_MSG( !IsOk(), false, "Inotify already initialized" );
6b8ef0b3 67
5cd99866
VZ
68 wxEventLoopBase *loop = wxEventLoopBase::GetActive();
69 wxCHECK_MSG( loop, false, "File system watcher needs an event loop" );
6b8ef0b3 70
5cd99866
VZ
71 m_ifd = inotify_init();
72 if ( m_ifd == -1 )
6b8ef0b3
VZ
73 {
74 wxLogSysError( _("Unable to create inotify instance") );
75 return false;
76 }
77
5cd99866
VZ
78 m_source = loop->AddSourceForFD
79 (
80 m_ifd,
81 m_handler,
82 wxEVENT_SOURCE_INPUT | wxEVENT_SOURCE_EXCEPTION
83 );
84
85 return m_source != NULL;
6b8ef0b3
VZ
86 }
87
5cd99866 88 void Close()
6b8ef0b3 89 {
5cd99866 90 wxCHECK_RET( IsOk(),
6b8ef0b3 91 "Inotify not initialized or invalid inotify descriptor" );
6b8ef0b3 92
5276b0a5 93 wxDELETE(m_source);
6b8ef0b3 94
5cd99866 95 if ( close(m_ifd) != 0 )
6b8ef0b3
VZ
96 {
97 wxLogSysError( _("Unable to close inotify instance") );
98 }
6b8ef0b3
VZ
99 }
100
101 virtual bool DoAdd(wxSharedPtr<wxFSWatchEntryUnix> watch)
102 {
103 wxCHECK_MSG( IsOk(), false,
104 "Inotify not initialized or invalid inotify descriptor" );
105
106 int wd = DoAddInotify(watch.get());
107 if (wd == -1)
108 {
109 wxLogSysError( _("Unable to add inotify watch") );
110 return false;
111 }
112
113 wxFSWatchEntryDescriptors::value_type val(wd, watch.get());
114 if (!m_watchMap.insert(val).second)
115 {
116 wxFAIL_MSG( wxString::Format( "Path %s is already watched",
117 watch->GetPath()) );
118 return false;
119 }
120
121 return true;
122 }
123
124 virtual bool DoRemove(wxSharedPtr<wxFSWatchEntryUnix> watch)
125 {
126 wxCHECK_MSG( IsOk(), false,
127 "Inotify not initialized or invalid inotify descriptor" );
128
129 int ret = DoRemoveInotify(watch.get());
130 if (ret == -1)
131 {
132 wxLogSysError( _("Unable to remove inotify watch") );
133 return false;
134 }
135
136 if (m_watchMap.erase(watch->GetWatchDescriptor()) != 1)
137 {
138 wxFAIL_MSG( wxString::Format("Path %s is not watched",
139 watch->GetPath()) );
140 }
3c03599e
VZ
141 // Cache the wd in case any events arrive late
142 m_staleDescriptors.Add(watch->GetWatchDescriptor());
143
6b8ef0b3
VZ
144 watch->SetWatchDescriptor(-1);
145 return true;
146 }
147
148 virtual bool RemoveAll()
149 {
150 wxFSWatchEntries::iterator it = m_watches.begin();
151 for ( ; it != m_watches.end(); ++it )
152 {
153 (void) DoRemove(it->second);
154 }
155 m_watches.clear();
156 return true;
157 }
158
159 int ReadEvents()
160 {
161 wxCHECK_MSG( IsOk(), -1,
162 "Inotify not initialized or invalid inotify descriptor" );
163
164 // read events
165 // TODO differentiate depending on params
166 char buf[128 * sizeof(inotify_event)];
167 int left = ReadEventsToBuf(buf, sizeof(buf));
168 if (left == -1)
169 return -1;
170
171 // left > 0, we have events
172 char* memory = buf;
173 int event_count = 0;
174 while (left > 0) // OPT checking 'memory' would suffice
175 {
176 event_count++;
177 inotify_event* e = (inotify_event*)memory;
178
179 // process one inotify_event
180 ProcessNativeEvent(*e);
181
182 int offset = sizeof(inotify_event) + e->len;
183 left -= offset;
184 memory += offset;
185 }
186
187 // take care of unmatched renames
188 ProcessRenames();
189
190 wxLogTrace(wxTRACE_FSWATCHER, "We had %d native events", event_count);
191 return event_count;
192 }
193
5cd99866 194 bool IsOk() const
6b8ef0b3 195 {
5cd99866 196 return m_source != NULL;
6b8ef0b3
VZ
197 }
198
199protected:
6b8ef0b3
VZ
200 int DoAddInotify(wxFSWatchEntry* watch)
201 {
202 int flags = Watcher2NativeFlags(watch->GetFlags());
5cd99866 203 int wd = inotify_add_watch(m_ifd, watch->GetPath().fn_str(), flags);
6b8ef0b3
VZ
204 // finally we can set watch descriptor
205 watch->SetWatchDescriptor(wd);
206 return wd;
207 }
208
209 int DoRemoveInotify(wxFSWatchEntry* watch)
210 {
5cd99866 211 return inotify_rm_watch(m_ifd, watch->GetWatchDescriptor());
6b8ef0b3
VZ
212 }
213
214 void ProcessNativeEvent(const inotify_event& inevt)
215 {
216 wxLogTrace(wxTRACE_FSWATCHER, InotifyEventToString(inevt));
217
218 // after removing inotify watch we get IN_IGNORED for it, but the watch
219 // will be already removed from our list at that time
220 if (inevt.mask & IN_IGNORED)
221 {
3c03599e
VZ
222 // It is now safe to remove it from the stale descriptors too, we
223 // won't get any more events for it.
0f306261
VZ
224 // However if we're here because a dir that we're still watching
225 // has just been deleted, its wd won't be on this list
226 const int pos = m_staleDescriptors.Index(inevt.wd);
227 if ( pos != wxNOT_FOUND )
228 {
229 m_staleDescriptors.RemoveAt(static_cast<size_t>(pos));
230 wxLogTrace(wxTRACE_FSWATCHER,
3c03599e 231 "Removed wd %i from the stale-wd cache", inevt.wd);
0f306261 232 }
6b8ef0b3
VZ
233 return;
234 }
235
236 // get watch entry for this event
237 wxFSWatchEntryDescriptors::iterator it = m_watchMap.find(inevt.wd);
8ded8d08
VZ
238
239 // wd will be -1 for IN_Q_OVERFLOW, which would trigger the wxFAIL_MSG
240 if (inevt.wd != -1)
3c03599e 241 {
8ded8d08 242 if (it == m_watchMap.end())
3c03599e 243 {
8ded8d08
VZ
244 // It's not in the map; check if was recently removed from it.
245 if (m_staleDescriptors.Index(inevt.wd) != wxNOT_FOUND)
246 {
247 wxLogTrace(wxTRACE_FSWATCHER,
248 "Got an event for stale wd %i", inevt.wd);
249 }
250 else
251 {
aa29d293
VZ
252 // In theory we shouldn't reach here. In practice, some
253 // events, e.g. IN_MODIFY, arrive just after the IN_IGNORED
254 // so their wd has already been discarded. Warn about them.
255 wxFileSystemWatcherEvent
256 event
257 (
258 wxFSW_EVENT_WARNING,
259 wxString::Format
260 (
261 _("Unexpected event for \"%s\": no "
262 "matching watch descriptor."),
263 inevt.len ? inevt.name : ""
264 )
265 );
266 SendEvent(event);
267
8ded8d08
VZ
268 }
269
270 // In any case, don't process this event: it's either for an
aa29d293 271 // already removed entry, or for an unknown one.
8ded8d08 272 return;
3c03599e 273 }
3c03599e 274 }
6b8ef0b3 275
6b8ef0b3
VZ
276 int nativeFlags = inevt.mask;
277 int flags = Native2WatcherFlags(nativeFlags);
278
279 // check out for error/warning condition
280 if (flags & wxFSW_EVENT_WARNING || flags & wxFSW_EVENT_ERROR)
281 {
1ec4e9c2 282 wxString errMsg = GetErrorDescription(nativeFlags);
6b8ef0b3
VZ
283 wxFileSystemWatcherEvent event(flags, errMsg);
284 SendEvent(event);
0a41f17e
VZ
285 return;
286 }
287
288 if (inevt.wd == -1)
289 {
290 // Although this is not supposed to happen, we seem to be getting
291 // occasional IN_ACCESS/IN_MODIFY events without valid watch value.
292 wxFileSystemWatcherEvent
293 event
294 (
295 wxFSW_EVENT_WARNING,
296 wxString::Format
297 (
298 _("Invalid inotify event for \"%s\""),
299 inevt.len ? inevt.name : ""
300 )
301 );
302 SendEvent(event);
8ded8d08 303 return;
6b8ef0b3 304 }
8ded8d08
VZ
305
306 wxFSWatchEntry& watch = *(it->second);
307
092e08a8
VZ
308 // Now IN_UNMOUNT. We must do so here, as it's not in the watch flags
309 if (nativeFlags & IN_UNMOUNT)
310 {
311 wxFileName path = GetEventPath(watch, inevt);
312 wxFileSystemWatcherEvent event(wxFSW_EVENT_UNMOUNT, path, path);
313 SendEvent(event);
314 }
6b8ef0b3
VZ
315 // filter out ignored events and those not asked for.
316 // we never filter out warnings or exceptions
317 else if ((flags == 0) || !(flags & watch.GetFlags()))
318 {
319 return;
320 }
20ffcd77
VZ
321
322 // Creation
323 // We need do something here only if the original watch was recursive;
324 // we don't watch a child dir itself inside a non-tree watch.
325 // We watch only dirs explicitly, so we don't want file IN_CREATEs.
326 // Distinguish by whether nativeFlags contain IN_ISDIR
327 else if ((nativeFlags & IN_CREATE) &&
328 (watch.GetType() == wxFSWPath_Tree) && (inevt.mask & IN_ISDIR))
329 {
330 wxFileName fn = GetEventPath(watch, inevt);
331 // Though it's a dir, fn treats it as a file. So:
332 fn.AssignDir(fn.GetFullPath());
333
334 if (m_watcher->AddAny(fn, wxFSW_EVENT_ALL,
335 wxFSWPath_Tree, watch.GetFilespec()))
336 {
337 // Tell the owner, in case it's interested
338 // If there's a filespec, assume he's not
339 if (watch.GetFilespec().empty())
340 {
341 wxFileSystemWatcherEvent event(flags, fn, fn);
342 SendEvent(event);
343 }
344 }
345 }
346
347 // Deletion
348 // We watch only dirs explicitly, so we don't want file IN_DELETEs.
349 // We obviously can't check using DirExists() as the object has been
350 // deleted; and nativeFlags here doesn't contain IN_ISDIR, even for
351 // a dir. Fortunately IN_DELETE_SELF doesn't happen for files. We need
352 // to do something here only inside a tree watch, or if it's the parent
353 // dir that's deleted. Otherwise let the parent dir cope
354 else if ((nativeFlags & IN_DELETE_SELF) &&
355 ((watch.GetType() == wxFSWPath_Dir) ||
356 (watch.GetType() == wxFSWPath_Tree)))
357 {
358 // We must remove the deleted directory from the map, so that
359 // DoRemoveInotify() isn't called on it in the future. Don't assert
360 // if the wd isn't found: repeated IN_DELETE_SELFs can occur
361 wxFileName fn = GetEventPath(watch, inevt);
362 wxString path(fn.GetPathWithSep());
363
364 if (m_watchMap.erase(inevt.wd) == 1)
365 {
366 // Delete from wxFileSystemWatcher
367 wxDynamicCast(m_watcher, wxInotifyFileSystemWatcher)->
368 OnDirDeleted(path);
369
370 // Now remove from our local list of watched items
371 wxFSWatchEntries::iterator wit =
372 m_watches.find(path);
373 if (wit != m_watches.end())
374 {
375 m_watches.erase(wit);
376 }
377
378 // Cache the wd in case any events arrive late
379 m_staleDescriptors.Add(inevt.wd);
380 }
381
382 // Tell the owner, in case it's interested
383 // If there's a filespec, assume he's not
384 if (watch.GetFilespec().empty())
385 {
386 wxFileSystemWatcherEvent event(flags, fn, fn);
387 SendEvent(event);
388 }
389 }
390
6b8ef0b3
VZ
391 // renames
392 else if (nativeFlags & IN_MOVE)
393 {
9f987630
VZ
394 // IN_MOVE events are produced in the following circumstances:
395 // * A move within a dir (what the user sees as a rename) gives an
396 // IN_MOVED_FROM and IN_MOVED_TO pair, each with the same path.
397 // * A move within watched dir foo/ e.g. from foo/bar1/ to foo/bar2/
398 // will also produce such a pair, but with different paths.
399 // * A move to baz/ will give only an IN_MOVED_FROM for foo/bar1;
400 // if baz/ is inside a different watch, that gets the IN_MOVED_TO.
401
402 // The first event to arrive, usually the IN_MOVED_FROM, is
403 // cached to await a matching IN_MOVED_TO; should none arrive, the
404 // unpaired event will be processed later in ProcessRenames().
7d1214cd
PC
405 wxInotifyCookies::iterator it2 = m_cookies.find(inevt.cookie);
406 if ( it2 == m_cookies.end() )
6b8ef0b3
VZ
407 {
408 int size = sizeof(inevt) + inevt.len;
409 inotify_event* e = (inotify_event*) operator new (size);
410 memcpy(e, &inevt, size);
411
412 wxInotifyCookies::value_type val(e->cookie, e);
413 m_cookies.insert(val);
414 }
415 else
416 {
7d1214cd 417 inotify_event& oldinevt = *(it2->second);
6b8ef0b3 418
6eef5763
VZ
419 // Tell the owner, in case it's interested
420 // If there's a filespec, assume he's not
421 if ( watch.GetFilespec().empty() )
6b8ef0b3 422 {
9f987630
VZ
423 // The the only way to know the path for the first event,
424 // normally the IN_MOVED_FROM, is to retrieve the watch
425 // corresponding to oldinevt. This is needed for a move
426 // within a watch.
427 wxFSWatchEntry* oldwatch;
428 wxFSWatchEntryDescriptors::iterator oldwatch_it =
429 m_watchMap.find(oldinevt.wd);
430 if (oldwatch_it != m_watchMap.end())
431 {
432 oldwatch = oldwatch_it->second;
433 }
434 else
435 {
436 wxLogTrace(wxTRACE_FSWATCHER,
437 "oldinevt's watch descriptor not in the watch map");
438 // For want of a better alternative, use 'watch'. That
439 // will work fine for renames, though not for moves
440 oldwatch = &watch;
441 }
442
6eef5763
VZ
443 wxFileSystemWatcherEvent event(flags);
444 if ( inevt.mask & IN_MOVED_FROM )
445 {
446 event.SetPath(GetEventPath(watch, inevt));
9f987630 447 event.SetNewPath(GetEventPath(*oldwatch, oldinevt));
6eef5763
VZ
448 }
449 else
450 {
9f987630 451 event.SetPath(GetEventPath(*oldwatch, oldinevt));
6eef5763
VZ
452 event.SetNewPath(GetEventPath(watch, inevt));
453 }
454 SendEvent(event);
6b8ef0b3 455 }
6b8ef0b3 456
7d1214cd 457 m_cookies.erase(it2);
6b8ef0b3
VZ
458 delete &oldinevt;
459 }
460 }
461 // every other kind of event
462 else
463 {
464 wxFileName path = GetEventPath(watch, inevt);
6eef5763
VZ
465 // For files, check that it matches any filespec
466 if ( MatchesFilespec(path, watch.GetFilespec()) )
467 {
468 wxFileSystemWatcherEvent event(flags, path, path);
469 SendEvent(event);
470 }
6b8ef0b3
VZ
471 }
472 }
473
474 void ProcessRenames()
475 {
9f987630
VZ
476 // After all of a batch of events has been processed, this deals with
477 // any still-unpaired IN_MOVED_FROM or IN_MOVED_TO events.
6b8ef0b3
VZ
478 wxInotifyCookies::iterator it = m_cookies.begin();
479 while ( it != m_cookies.end() )
480 {
481 inotify_event& inevt = *(it->second);
482
483 wxLogTrace(wxTRACE_FSWATCHER, "Processing pending rename events");
484 wxLogTrace(wxTRACE_FSWATCHER, InotifyEventToString(inevt));
485
486 // get watch entry for this event
487 wxFSWatchEntryDescriptors::iterator wit = m_watchMap.find(inevt.wd);
dab61056
VZ
488 if (wit == m_watchMap.end())
489 {
490 wxLogTrace(wxTRACE_FSWATCHER,
491 "Watch descriptor not present in the watch map!");
492 }
493 else
6eef5763 494 {
dab61056
VZ
495 // Tell the owner, in case it's interested
496 // If there's a filespec, assume he's not
497 wxFSWatchEntry& watch = *(wit->second);
498 if ( watch.GetFilespec().empty() )
6eef5763 499 {
dab61056
VZ
500 int flags = Native2WatcherFlags(inevt.mask);
501 wxFileName path = GetEventPath(watch, inevt);
502 {
503 wxFileSystemWatcherEvent event(flags, path, path);
504 SendEvent(event);
505 }
6eef5763
VZ
506 }
507 }
6b8ef0b3
VZ
508
509 m_cookies.erase(it);
510 delete &inevt;
511 it = m_cookies.begin();
512 }
513 }
514
515 void SendEvent(wxFileSystemWatcherEvent& evt)
516 {
517 wxLogTrace(wxTRACE_FSWATCHER, evt.ToString());
518 m_watcher->GetOwner()->ProcessEvent(evt);
519 }
520
521 int ReadEventsToBuf(char* buf, int size)
522 {
523 wxCHECK_MSG( IsOk(), false,
524 "Inotify not initialized or invalid inotify descriptor" );
525
526 memset(buf, 0, size);
5cd99866 527 ssize_t left = read(m_ifd, buf, size);
6b8ef0b3
VZ
528 if (left == -1)
529 {
530 wxLogSysError(_("Unable to read from inotify descriptor"));
531 return -1;
532 }
533 else if (left == 0)
534 {
535 wxLogWarning(_("EOF while reading from inotify descriptor"));
536 return -1;
537 }
538
539 return left;
540 }
541
542 static wxString InotifyEventToString(const inotify_event& inevt)
543 {
544 wxString mask = (inevt.mask & IN_ISDIR) ?
545 wxString::Format("IS_DIR | %u", inevt.mask & ~IN_ISDIR) :
546 wxString::Format("%u", inevt.mask);
41e02755
PC
547 const char* name = "";
548 if (inevt.len)
549 name = inevt.name;
6b8ef0b3
VZ
550 return wxString::Format("Event: wd=%d, mask=%s, cookie=%u, len=%u, "
551 "name=%s", inevt.wd, mask, inevt.cookie,
41e02755 552 inevt.len, name);
6b8ef0b3
VZ
553 }
554
555 static wxFileName GetEventPath(const wxFSWatchEntry& watch,
556 const inotify_event& inevt)
557 {
558 // only when dir is watched, we have non-empty e.name
559 wxFileName path = watch.GetPath();
41e02755 560 if (path.IsDir() && inevt.len)
6b8ef0b3
VZ
561 {
562 path = wxFileName(path.GetPath(), inevt.name);
563 }
564 return path;
565 }
566
b77bb705 567 static int Watcher2NativeFlags(int flags)
6b8ef0b3 568 {
b77bb705
VZ
569 // Start with the standard case of wanting all events
570 if (flags == wxFSW_EVENT_ALL)
571 {
572 return IN_ALL_EVENTS;
573 }
574
575 static const int flag_mapping[][2] = {
092e08a8
VZ
576 { wxFSW_EVENT_ACCESS, IN_ACCESS },
577 { wxFSW_EVENT_MODIFY, IN_MODIFY },
578 { wxFSW_EVENT_ATTRIB, IN_ATTRIB },
579 { wxFSW_EVENT_RENAME, IN_MOVE },
580 { wxFSW_EVENT_CREATE, IN_CREATE },
581 { wxFSW_EVENT_DELETE, IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF },
582 { wxFSW_EVENT_UNMOUNT, IN_UNMOUNT }
b77bb705
VZ
583 // wxFSW_EVENT_ERROR/WARNING make no sense here
584 };
585
586 int native_flags = 0;
587 for ( unsigned int i=0; i < WXSIZEOF(flag_mapping); ++i)
588 {
589 if (flags & flag_mapping[i][0])
590 native_flags |= flag_mapping[i][1];
591 }
592
593 return native_flags;
6b8ef0b3
VZ
594 }
595
596 static int Native2WatcherFlags(int flags)
597 {
598 static const int flag_mapping[][2] = {
599 { IN_ACCESS, wxFSW_EVENT_ACCESS }, // generated during read!
600 { IN_MODIFY, wxFSW_EVENT_MODIFY },
f31f9900 601 { IN_ATTRIB, wxFSW_EVENT_ATTRIB },
6b8ef0b3
VZ
602 { IN_CLOSE_WRITE, 0 },
603 { IN_CLOSE_NOWRITE, 0 },
604 { IN_OPEN, 0 },
605 { IN_MOVED_FROM, wxFSW_EVENT_RENAME },
606 { IN_MOVED_TO, wxFSW_EVENT_RENAME },
607 { IN_CREATE, wxFSW_EVENT_CREATE },
608 { IN_DELETE, wxFSW_EVENT_DELETE },
609 { IN_DELETE_SELF, wxFSW_EVENT_DELETE },
610 { IN_MOVE_SELF, wxFSW_EVENT_DELETE },
611
092e08a8 612 { IN_UNMOUNT, wxFSW_EVENT_UNMOUNT},
6b8ef0b3
VZ
613 { IN_Q_OVERFLOW, wxFSW_EVENT_WARNING},
614
b77bb705 615 // ignored, because this is generated mainly by watcher::Remove()
6b8ef0b3
VZ
616 { IN_IGNORED, 0 }
617 };
618
619 unsigned int i=0;
620 for ( ; i < WXSIZEOF(flag_mapping); ++i) {
621 // in this mapping multiple flags at once don't happen
622 if (flags & flag_mapping[i][0])
623 return flag_mapping[i][1];
624 }
625
626 // never reached
627 wxFAIL_MSG(wxString::Format("Unknown inotify event mask %u", flags));
628 return -1;
629 }
630
631 /**
632 * Returns error description for specified inotify mask
633 */
634 static const wxString GetErrorDescription(int flag)
635 {
636 switch ( flag )
637 {
6b8ef0b3
VZ
638 case IN_Q_OVERFLOW:
639 return _("Event queue overflowed");
640 }
641
642 // never reached
643 wxFAIL_MSG(wxString::Format("Unknown inotify event mask %u", flag));
644 return wxEmptyString;
645 }
646
647 wxFSWSourceHandler* m_handler; // handler for inotify event source
648 wxFSWatchEntryDescriptors m_watchMap; // inotify wd=>wxFSWatchEntry* map
3c03599e 649 wxArrayInt m_staleDescriptors; // stores recently-removed watches
6b8ef0b3 650 wxInotifyCookies m_cookies; // map to track renames
5cd99866
VZ
651 wxEventLoopSource* m_source; // our event loop source
652
653 // file descriptor created by inotify_init()
654 int m_ifd;
6b8ef0b3
VZ
655};
656
657
658// ============================================================================
659// wxFSWSourceHandler implementation
660// ============================================================================
661
662// once we get signaled to read, actuall event reading occurs
663void wxFSWSourceHandler::OnReadWaiting()
664{
665 wxLogTrace(wxTRACE_FSWATCHER, "--- OnReadWaiting ---");
666 m_service->ReadEvents();
667}
668
669void wxFSWSourceHandler::OnWriteWaiting()
670{
671 wxFAIL_MSG("We never write to inotify descriptor.");
672}
673
674void wxFSWSourceHandler::OnExceptionWaiting()
675{
676 wxFAIL_MSG("We never receive exceptions on inotify descriptor.");
677}
678
679
680// ============================================================================
681// wxInotifyFileSystemWatcher implementation
682// ============================================================================
683
684wxInotifyFileSystemWatcher::wxInotifyFileSystemWatcher()
685 : wxFileSystemWatcherBase()
686{
687 Init();
688}
689
690wxInotifyFileSystemWatcher::wxInotifyFileSystemWatcher(const wxFileName& path,
691 int events)
692 : wxFileSystemWatcherBase()
693{
694 if (!Init())
695 {
696 if (m_service)
697 delete m_service;
698 return;
699 }
700
701 Add(path, events);
702}
703
704wxInotifyFileSystemWatcher::~wxInotifyFileSystemWatcher()
705{
706}
707
708bool wxInotifyFileSystemWatcher::Init()
709{
710 m_service = new wxFSWatcherImplUnix(this);
711 return m_service->Init();
712}
713
20ffcd77
VZ
714void wxInotifyFileSystemWatcher::OnDirDeleted(const wxString& path)
715{
716 if (!path.empty())
717 {
718 wxFSWatchInfoMap::iterator it = m_watches.find(path);
719 wxCHECK_RET(it != m_watches.end(),
720 wxString::Format("Path '%s' is not watched", path));
721
722 // path has been deleted, so we must forget it whatever its refcount
723 m_watches.erase(it);
724 }
725}
726
6b8ef0b3
VZ
727#endif // wxHAS_INOTIFY
728
729#endif // wxUSE_FSWATCHER