~registry/dolphin-emu/triforce

« back to all changes in this revision

Viewing changes to Externals/wxWidgets3/src/unix/fswatcher_inotify.cpp

  • Committer: Sérgio Benjamim
  • Date: 2015-02-13 05:54:40 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20150213055440-ey2rt3sjpy27km78
Dolphin Triforce branch from code.google, commit b957980 (4.0-315).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/////////////////////////////////////////////////////////////////////////////
 
2
// Name:        src/unix/fswatcher_inotify.cpp
 
3
// Purpose:     inotify-based wxFileSystemWatcher implementation
 
4
// Author:      Bartosz Bekier
 
5
// Created:     2009-05-26
 
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
 
32
WX_DECLARE_HASH_MAP(int, wxFSWatchEntry*, wxIntegerHash, wxIntegerEqual,
 
33
                                              wxFSWatchEntryDescriptors);
 
34
 
 
35
// inotify event cookie => inotify_event* map
 
36
WX_DECLARE_HASH_MAP(int, inotify_event*, wxIntegerHash, wxIntegerEqual,
 
37
                                                      wxInotifyCookies);
 
38
 
 
39
/**
 
40
 * Helper class encapsulating inotify mechanism
 
41
 */
 
42
class wxFSWatcherImplUnix : public wxFSWatcherImpl
 
43
{
 
44
public:
 
45
    wxFSWatcherImplUnix(wxFileSystemWatcherBase* watcher) :
 
46
        wxFSWatcherImpl(watcher),
 
47
        m_source(NULL),
 
48
        m_ifd(-1)
 
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" );
 
67
 
 
68
        wxEventLoopBase *loop = wxEventLoopBase::GetActive();
 
69
        wxCHECK_MSG( loop, false, "File system watcher needs an event loop" );
 
70
 
 
71
        m_ifd = inotify_init();
 
72
        if ( m_ifd == -1 )
 
73
        {
 
74
            wxLogSysError( _("Unable to create inotify instance") );
 
75
            return false;
 
76
        }
 
77
 
 
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;
 
86
    }
 
87
 
 
88
    void Close()
 
89
    {
 
90
        wxCHECK_RET( IsOk(),
 
91
                    "Inotify not initialized or invalid inotify descriptor" );
 
92
 
 
93
        wxDELETE(m_source);
 
94
 
 
95
        if ( close(m_ifd) != 0 )
 
96
        {
 
97
            wxLogSysError( _("Unable to close inotify instance") );
 
98
        }
 
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
        }
 
141
        // Cache the wd in case any events arrive late
 
142
        m_staleDescriptors.Add(watch->GetWatchDescriptor());
 
143
 
 
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
 
 
194
    bool IsOk() const
 
195
    {
 
196
        return m_source != NULL;
 
197
    }
 
198
 
 
199
protected:
 
200
    int DoAddInotify(wxFSWatchEntry* watch)
 
201
    {
 
202
        int flags = Watcher2NativeFlags(watch->GetFlags());
 
203
        int wd = inotify_add_watch(m_ifd, watch->GetPath().fn_str(), flags);
 
204
        // finally we can set watch descriptor
 
205
        watch->SetWatchDescriptor(wd);
 
206
        return wd;
 
207
    }
 
208
 
 
209
    int DoRemoveInotify(wxFSWatchEntry* watch)
 
210
    {
 
211
        return inotify_rm_watch(m_ifd, watch->GetWatchDescriptor());
 
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
        {
 
222
            // It is now safe to remove it from the stale descriptors too, we
 
223
            // won't get any more events for it.
 
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,
 
231
                       "Removed wd %i from the stale-wd cache", inevt.wd);
 
232
            }
 
233
            return;
 
234
        }
 
235
 
 
236
        // get watch entry for this event
 
237
        wxFSWatchEntryDescriptors::iterator it = m_watchMap.find(inevt.wd);
 
238
 
 
239
        // wd will be -1 for IN_Q_OVERFLOW, which would trigger the wxFAIL_MSG
 
240
        if (inevt.wd != -1)
 
241
        {
 
242
            if (it == m_watchMap.end())
 
243
            {
 
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
                {
 
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
 
 
268
                }
 
269
 
 
270
                // In any case, don't process this event: it's either for an
 
271
                // already removed entry, or for an unknown one.
 
272
                return;
 
273
            }
 
274
        }
 
275
 
 
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
        {
 
282
            wxString errMsg = GetErrorDescription(nativeFlags);
 
283
            wxFileSystemWatcherEvent event(flags, errMsg);
 
284
            SendEvent(event);
 
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);
 
303
            return;
 
304
        }
 
305
 
 
306
        wxFSWatchEntry& watch = *(it->second);
 
307
 
 
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
        }
 
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
        }
 
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
 
 
391
        // renames
 
392
        else if (nativeFlags & IN_MOVE)
 
393
        {
 
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().
 
405
            wxInotifyCookies::iterator it2 = m_cookies.find(inevt.cookie);
 
406
            if ( it2 == m_cookies.end() )
 
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
            {
 
417
                inotify_event& oldinevt = *(it2->second);
 
418
 
 
419
                // Tell the owner, in case it's interested
 
420
                // If there's a filespec, assume he's not
 
421
                if ( watch.GetFilespec().empty() )
 
422
                {
 
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
 
 
443
                    wxFileSystemWatcherEvent event(flags);
 
444
                    if ( inevt.mask & IN_MOVED_FROM )
 
445
                    {
 
446
                        event.SetPath(GetEventPath(watch, inevt));
 
447
                        event.SetNewPath(GetEventPath(*oldwatch, oldinevt));
 
448
                    }
 
449
                    else
 
450
                    {
 
451
                        event.SetPath(GetEventPath(*oldwatch, oldinevt));
 
452
                        event.SetNewPath(GetEventPath(watch, inevt));
 
453
                    }
 
454
                    SendEvent(event);
 
455
                }
 
456
 
 
457
                m_cookies.erase(it2);
 
458
                delete &oldinevt;
 
459
            }
 
460
        }
 
461
        // every other kind of event
 
462
        else
 
463
        {
 
464
            wxFileName path = GetEventPath(watch, inevt);
 
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
            }
 
471
        }
 
472
    }
 
473
 
 
474
    void ProcessRenames()
 
475
    {
 
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.
 
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);
 
488
            if (wit == m_watchMap.end())
 
489
            {
 
490
                wxLogTrace(wxTRACE_FSWATCHER,
 
491
                            "Watch descriptor not present in the watch map!");
 
492
            }
 
493
            else
 
494
            {
 
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() )
 
499
                {
 
500
                    int flags = Native2WatcherFlags(inevt.mask);
 
501
                    wxFileName path = GetEventPath(watch, inevt);
 
502
                    {
 
503
                        wxFileSystemWatcherEvent event(flags, path, path);
 
504
                        SendEvent(event);
 
505
                    }
 
506
                }
 
507
            }
 
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);
 
527
        ssize_t left = read(m_ifd, buf, size);
 
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);
 
547
        const char* name = "";
 
548
        if (inevt.len)
 
549
            name = inevt.name;
 
550
        return wxString::Format("Event: wd=%d, mask=%s, cookie=%u, len=%u, "
 
551
                                "name=%s", inevt.wd, mask, inevt.cookie,
 
552
                                inevt.len, name);
 
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();
 
560
        if (path.IsDir() && inevt.len)
 
561
        {
 
562
            path = wxFileName(path.GetPath(), inevt.name);
 
563
        }
 
564
        return path;
 
565
    }
 
566
 
 
567
    static int Watcher2NativeFlags(int flags)
 
568
    {
 
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] = {
 
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 }
 
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;
 
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 },
 
601
            { IN_ATTRIB,        wxFSW_EVENT_ATTRIB },
 
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
 
 
612
            { IN_UNMOUNT,       wxFSW_EVENT_UNMOUNT},
 
613
            { IN_Q_OVERFLOW,    wxFSW_EVENT_WARNING},
 
614
 
 
615
            // ignored, because this is generated mainly by watcher::Remove()
 
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
        {
 
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
 
649
    wxArrayInt m_staleDescriptors;        // stores recently-removed watches
 
650
    wxInotifyCookies m_cookies;           // map to track renames
 
651
    wxEventLoopSource* m_source;          // our event loop source
 
652
 
 
653
    // file descriptor created by inotify_init()
 
654
    int m_ifd;
 
655
};
 
656
 
 
657
 
 
658
// ============================================================================
 
659
// wxFSWSourceHandler implementation
 
660
// ============================================================================
 
661
 
 
662
// once we get signaled to read, actuall event reading occurs
 
663
void wxFSWSourceHandler::OnReadWaiting()
 
664
{
 
665
    wxLogTrace(wxTRACE_FSWATCHER, "--- OnReadWaiting ---");
 
666
    m_service->ReadEvents();
 
667
}
 
668
 
 
669
void wxFSWSourceHandler::OnWriteWaiting()
 
670
{
 
671
    wxFAIL_MSG("We never write to inotify descriptor.");
 
672
}
 
673
 
 
674
void wxFSWSourceHandler::OnExceptionWaiting()
 
675
{
 
676
    wxFAIL_MSG("We never receive exceptions on inotify descriptor.");
 
677
}
 
678
 
 
679
 
 
680
// ============================================================================
 
681
// wxInotifyFileSystemWatcher implementation
 
682
// ============================================================================
 
683
 
 
684
wxInotifyFileSystemWatcher::wxInotifyFileSystemWatcher()
 
685
    : wxFileSystemWatcherBase()
 
686
{
 
687
    Init();
 
688
}
 
689
 
 
690
wxInotifyFileSystemWatcher::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
 
 
704
wxInotifyFileSystemWatcher::~wxInotifyFileSystemWatcher()
 
705
{
 
706
}
 
707
 
 
708
bool wxInotifyFileSystemWatcher::Init()
 
709
{
 
710
    m_service = new wxFSWatcherImplUnix(this);
 
711
    return m_service->Init();
 
712
}
 
713
 
 
714
void 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
 
 
727
#endif // wxHAS_INOTIFY
 
728
 
 
729
#endif // wxUSE_FSWATCHER