mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-17 15:21:16 +00:00
331 lines
9.7 KiB
C++
331 lines
9.7 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/fswatchercmn.cpp
|
|
// Purpose: wxMswFileSystemWatcher
|
|
// Author: Bartosz Bekier
|
|
// Created: 2009-05-26
|
|
// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_FSWATCHER
|
|
|
|
#include "wx/fswatcher.h"
|
|
#include "wx/private/fswatcher.h"
|
|
|
|
// ============================================================================
|
|
// helpers
|
|
// ============================================================================
|
|
|
|
wxDEFINE_EVENT(wxEVT_FSWATCHER, wxFileSystemWatcherEvent);
|
|
|
|
static wxString GetFSWEventChangeTypeName(int type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case wxFSW_EVENT_CREATE:
|
|
return "CREATE";
|
|
case wxFSW_EVENT_DELETE:
|
|
return "DELETE";
|
|
case wxFSW_EVENT_RENAME:
|
|
return "RENAME";
|
|
case wxFSW_EVENT_MODIFY:
|
|
return "MODIFY";
|
|
case wxFSW_EVENT_ACCESS:
|
|
return "ACCESS";
|
|
case wxFSW_EVENT_ATTRIB: // Currently this is wxGTK-only
|
|
return "ATTRIBUTE";
|
|
#ifdef wxHAS_INOTIFY
|
|
case wxFSW_EVENT_UNMOUNT: // Currently this is wxGTK-only
|
|
return "UNMOUNT";
|
|
#endif
|
|
case wxFSW_EVENT_WARNING:
|
|
return "WARNING";
|
|
case wxFSW_EVENT_ERROR:
|
|
return "ERROR";
|
|
}
|
|
|
|
// should never be reached!
|
|
wxFAIL_MSG("Unknown change type");
|
|
return "INVALID_TYPE";
|
|
}
|
|
|
|
|
|
// ============================================================================
|
|
// wxFileSystemWatcherEvent implementation
|
|
// ============================================================================
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxFileSystemWatcherEvent, wxEvent);
|
|
|
|
wxString wxFileSystemWatcherEvent::ToString() const
|
|
{
|
|
if (IsError())
|
|
{
|
|
return wxString::Format("FSW_EVT type=%d (%s) message='%s'", m_changeType,
|
|
GetFSWEventChangeTypeName(m_changeType), GetErrorDescription());
|
|
}
|
|
return wxString::Format("FSW_EVT type=%d (%s) path='%s'", m_changeType,
|
|
GetFSWEventChangeTypeName(m_changeType), GetPath().GetFullPath());
|
|
}
|
|
|
|
|
|
// ============================================================================
|
|
// wxFileSystemWatcherEvent implementation
|
|
// ============================================================================
|
|
|
|
wxFileSystemWatcherBase::wxFileSystemWatcherBase() :
|
|
m_service(0), m_owner(this)
|
|
{
|
|
}
|
|
|
|
wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
|
|
{
|
|
RemoveAll();
|
|
if (m_service)
|
|
{
|
|
delete m_service;
|
|
}
|
|
}
|
|
|
|
bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
|
{
|
|
wxFSWPathType type = wxFSWPath_None;
|
|
if ( path.FileExists() )
|
|
{
|
|
type = wxFSWPath_File;
|
|
}
|
|
else if ( path.DirExists() )
|
|
{
|
|
type = wxFSWPath_Dir;
|
|
}
|
|
else
|
|
{
|
|
// Don't overreact to being passed a non-existent item. It may have
|
|
// only just been deleted, in which case doing nothing is correct
|
|
wxLogTrace(wxTRACE_FSWATCHER,
|
|
"Can't monitor non-existent path \"%s\" for changes.",
|
|
path.GetFullPath());
|
|
return false;
|
|
}
|
|
|
|
return AddAny(path, events, type);
|
|
}
|
|
|
|
bool
|
|
wxFileSystemWatcherBase::AddAny(const wxFileName& path,
|
|
int events,
|
|
wxFSWPathType type,
|
|
const wxString& filespec)
|
|
{
|
|
wxString canonical = GetCanonicalPath(path);
|
|
if (canonical.IsEmpty())
|
|
return false;
|
|
|
|
// adding a path in a platform specific way
|
|
wxFSWatchInfo watch(canonical, events, type, filespec);
|
|
if ( !m_service->Add(watch) )
|
|
return false;
|
|
|
|
// on success, either add path to our 'watch-list'
|
|
// or, if already watched, inc the refcount. This may happen if
|
|
// a dir is Add()ed, then later AddTree() is called on a parent dir
|
|
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
|
|
if ( it == m_watches.end() )
|
|
{
|
|
wxFSWatchInfoMap::value_type val(canonical, watch);
|
|
m_watches.insert(val);
|
|
}
|
|
else
|
|
{
|
|
wxFSWatchInfo& watch = it->second;
|
|
int count = watch.IncRef();
|
|
wxLogTrace(wxTRACE_FSWATCHER,
|
|
"'%s' is now watched %d times", canonical, count);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
|
|
{
|
|
// args validation & consistency checks
|
|
wxString canonical = GetCanonicalPath(path);
|
|
if (canonical.IsEmpty())
|
|
return false;
|
|
|
|
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
|
|
wxCHECK_MSG(it != m_watches.end(), false,
|
|
wxString::Format("Path '%s' is not watched", canonical));
|
|
|
|
// Decrement the watch's refcount and remove from watch-list if 0
|
|
bool ret = true;
|
|
wxFSWatchInfo& watch = it->second;
|
|
if ( !watch.DecRef() )
|
|
{
|
|
// remove in a platform specific way
|
|
ret = m_service->Remove(watch);
|
|
|
|
m_watches.erase(it);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
|
|
const wxString& filespec)
|
|
{
|
|
if (!path.DirExists())
|
|
return false;
|
|
|
|
// OPT could be optimised if we stored information about relationships
|
|
// between paths
|
|
class AddTraverser : public wxDirTraverser
|
|
{
|
|
public:
|
|
AddTraverser(wxFileSystemWatcherBase* watcher, int events,
|
|
const wxString& filespec) :
|
|
m_watcher(watcher), m_events(events), m_filespec(filespec)
|
|
{
|
|
}
|
|
|
|
virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
|
|
{
|
|
// There is no need to watch individual files as we watch the
|
|
// parent directory which will notify us about any changes in them.
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
|
|
virtual wxDirTraverseResult OnDir(const wxString& dirname)
|
|
{
|
|
if ( m_watcher->AddAny(wxFileName::DirName(dirname),
|
|
m_events, wxFSWPath_Tree, m_filespec) )
|
|
{
|
|
wxLogTrace(wxTRACE_FSWATCHER,
|
|
"--- AddTree adding directory '%s' ---", dirname);
|
|
}
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
|
|
private:
|
|
wxFileSystemWatcherBase* m_watcher;
|
|
int m_events;
|
|
wxString m_filespec;
|
|
};
|
|
|
|
wxDir dir(path.GetFullPath());
|
|
// Prevent asserts or infinite loops in trees containing symlinks
|
|
int flags = wxDIR_DIRS;
|
|
if ( !path.ShouldFollowLink() )
|
|
{
|
|
flags |= wxDIR_NO_FOLLOW;
|
|
}
|
|
AddTraverser traverser(this, events, filespec);
|
|
dir.Traverse(traverser, filespec, flags);
|
|
|
|
// Add the path itself explicitly as Traverse() doesn't return it.
|
|
AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
|
|
{
|
|
if (!path.DirExists())
|
|
return false;
|
|
|
|
// OPT could be optimised if we stored information about relationships
|
|
// between paths
|
|
class RemoveTraverser : public wxDirTraverser
|
|
{
|
|
public:
|
|
RemoveTraverser(wxFileSystemWatcherBase* watcher,
|
|
const wxString& filespec) :
|
|
m_watcher(watcher), m_filespec(filespec)
|
|
{
|
|
}
|
|
|
|
virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
|
|
{
|
|
// We never watch the individual files when watching the tree, so
|
|
// nothing to do here.
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
|
|
virtual wxDirTraverseResult OnDir(const wxString& dirname)
|
|
{
|
|
m_watcher->Remove(wxFileName::DirName(dirname));
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
|
|
private:
|
|
wxFileSystemWatcherBase* m_watcher;
|
|
wxString m_filespec;
|
|
};
|
|
|
|
// If AddTree() used a filespec, we must use the same one
|
|
wxString canonical = GetCanonicalPath(path);
|
|
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
|
|
wxCHECK_MSG( it != m_watches.end(), false,
|
|
wxString::Format("Path '%s' is not watched", canonical) );
|
|
wxFSWatchInfo watch = it->second;
|
|
const wxString filespec = watch.GetFilespec();
|
|
|
|
#if defined(__WINDOWS__)
|
|
// When there's no filespec, the wxMSW AddTree() would have set a watch
|
|
// on only the passed 'path'. We must therefore remove only this
|
|
if (filespec.empty())
|
|
{
|
|
return Remove(path);
|
|
}
|
|
// Otherwise fall through to the generic implementation
|
|
#endif // __WINDOWS__
|
|
|
|
wxDir dir(path.GetFullPath());
|
|
// AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
|
|
// infinite loops in trees containing symlinks. We need to do the same
|
|
// or we'll try to remove unwatched items. Let's hope the caller used
|
|
// the same ShouldFollowLink() setting as in AddTree()...
|
|
int flags = wxDIR_DIRS;
|
|
if ( !path.ShouldFollowLink() )
|
|
{
|
|
flags |= wxDIR_NO_FOLLOW;
|
|
}
|
|
RemoveTraverser traverser(this, filespec);
|
|
dir.Traverse(traverser, filespec, flags);
|
|
|
|
// As in AddTree() above, handle the path itself explicitly.
|
|
Remove(path);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxFileSystemWatcherBase::RemoveAll()
|
|
{
|
|
m_service->RemoveAll();
|
|
m_watches.clear();
|
|
return true;
|
|
}
|
|
|
|
int wxFileSystemWatcherBase::GetWatchedPathsCount() const
|
|
{
|
|
return m_watches.size();
|
|
}
|
|
|
|
int wxFileSystemWatcherBase::GetWatchedPaths(wxArrayString* paths) const
|
|
{
|
|
wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths");
|
|
|
|
wxFSWatchInfoMap::const_iterator it = m_watches.begin();
|
|
for ( ; it != m_watches.end(); ++it)
|
|
{
|
|
paths->push_back(it->first);
|
|
}
|
|
|
|
return m_watches.size();
|
|
}
|
|
|
|
#endif // wxUSE_FSWATCHER
|