kdecore: KDirWatch rewrite

I rewrote QFileSystemWatcher some time ago for it to be able to watch
non-existing directories, did rewrite KDirWatch too to not use FAM
and inotify making the created() and deleted() signals non-operational
(with the plan to maybe implement them in QFileSystemWatcher but that
will be hack-ish for directories with stat()-based implementation)

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-12-21 00:19:19 +02:00
parent 1cf9b433f7
commit 03aba012a0
5 changed files with 97 additions and 302 deletions

View file

@ -1,24 +1,19 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
Copyright (C) 2008 Rafal Rzepecki <divided.mind@gmail.com>
Copyright (C) 2010 David Faure <faure@kde.org>
Copyright (C) 2019 Ivailo Monev <xakepa10@laimg.moc>
/* This file is part of the KDE libraries
Copyright (C) 2019 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdirwatch.h"
@ -26,7 +21,6 @@
#include "kde_file.h"
#include <kdebug.h>
#include <QStringList>
#include <QDir>
KDirWatchPrivate::KDirWatchPrivate()
@ -43,11 +37,12 @@ Q_GLOBAL_STATIC(KDirWatch, globalWatch)
KDirWatch* KDirWatch::self()
{
return globalWatch();
return globalWatch();
}
KDirWatch::KDirWatch (QObject* parent)
: QObject(parent), d(new KDirWatchPrivate())
KDirWatch::KDirWatch(QObject* parent)
: QObject(parent),
d(new KDirWatchPrivate())
{
connect(d->watcher, SIGNAL(directoryChanged(QString)), this, SLOT(setDirty(QString)));
connect(d->watcher, SIGNAL(fileChanged(QString)), this, SLOT(setDirty(QString)));
@ -60,7 +55,7 @@ KDirWatch::~KDirWatch()
delete d;
}
void KDirWatch::addDir(const QString& path, WatchModes watchModes)
void KDirWatch::addDir(const QString &path)
{
if (path.isEmpty() || path.startsWith(QLatin1String("/dev"))) {
return; // Don't even go there.
@ -68,35 +63,19 @@ void KDirWatch::addDir(const QString& path, WatchModes watchModes)
return;
}
if (watchModes & WatchDirOnly || watchModes == WatchDirOnly) {
d->watcher->addPath(path);
kDebug(7001) << "watching directory" << path;
// watching non-existing directory requires a trailing slash
if (!path.endsWith(QDir::separator())) {
const QString dirpath = path + QDir::separator();
d->watcheddirs.append(dirpath);
d->watcher->addPath(dirpath);
return;
}
kDebug(7001) << "listing" << path << watchModes;
QDir::Filters filters = QDir::NoDotAndDotDot;
if (watchModes & WatchFiles) {
filters |= QDir::Files;
}
if (watchModes & WatchSubDirs) {
filters |= QDir::Dirs;
}
QDir dir(path);
if (!dir.exists()) {
// try to watch the parent directory
dir.cdUp();
}
foreach(const QFileInfo &info, dir.entryInfoList(filters)) {
if (info.isDir()) {
addDir(info.absoluteFilePath(), watchModes);
} else {
d->watcher->addPath(info.absoluteFilePath());
}
}
d->watcheddirs.append(path);
d->watcher->addPath(path);
}
void KDirWatch::addFile(const QString& path)
void KDirWatch::addFile(const QString &path)
{
if (path.isEmpty() || path.startsWith(QLatin1String("/dev"))) {
return; // Don't even go there.
@ -104,16 +83,18 @@ void KDirWatch::addFile(const QString& path)
return;
}
QFileInfo info(path);
if (!info.exists() && !info.isDir()) {
// try to watch the parent directory
d->watcher->addPath(info.path());
if (QDir(path).exists()) {
// trying to add dir as file, huh?
addDir(path);
return;
}
kDebug(7001) << "watching file" << path;
d->watchedfiles.append(path);
d->watcher->addPath(path);
}
void KDirWatch::removeDir(const QString& path)
void KDirWatch::removeDir(const QString &path)
{
d->watcher->removePath(path);
}
@ -128,32 +109,19 @@ bool KDirWatch::contains(const QString &path) const
return (d->watcher->files().contains(path) || d->watcher->directories().contains(path));
}
void KDirWatch::setCreated(const QString &file)
{
kDebug(7001) << objectName() << "emitting created" << file;
emit created(file);
}
void KDirWatch::setDirty(const QString &file)
{
kDebug(7001) << objectName() << "emitting dirty" << file;
emit dirty(file);
// a simple solution for a complex problem - KConfig requires proper created/deleted events
// notification because INI backend uses KSaveFile which moves (renames) backup to original
// file but currently there is not. the proper solution is to watch exisiting configuration
// directories for changes (and future creation of such) and emit signals for file events
KDE_struct_stat statbuf;
if (KDE::stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
kDebug(7001) << "emitting dirty" << file;
// QFileSystemWatcher removes the file/dir from the watched list when it is deleted, put it
// back so that events are emited in case it is created
if (d->watcheddirs.contains(file)) {
addDir(file);
} else {
addFile(file);
}
}
void KDirWatch::setDeleted(const QString &file)
{
kDebug(7001) << objectName() << "emitting deleted" << file;
emit deleted(file);
emit dirty(file);
}
#include "moc_kdirwatch.cpp"
//sven

View file

@ -1,24 +1,24 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
/* This file is part of the KDE libraries
Copyright (C) 2019 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDIRWATCH_H
#define KDIRWATCH_H
#include <QtCore/qdatetime.h>
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
@ -29,55 +29,19 @@ class KDirWatchPrivate;
/**
* @short Class for watching directory and file changes.
*
* Watch directories and files for changes.
* The watched directories or files has to exist.
*
* When a watched directory is changed, i.e. when files therein are
* created or deleted, KDirWatch will emit the signal dirty().
*
* When a watched, but previously not existing directory gets created,
* KDirWatch will emit the signal created().
*
* When a watched directory gets deleted, KDirWatch will emit the
* signal deleted(). The directory is still watched for new
* creation.
*
* Both created() and deleted() are kept for compatibility and you
* should not rely on them as they are used as proxy for other
* classes. Instead you can use KDirNotify or KDirLister which
* provide a lot more functionality than KDirWatch.
*
* When a watched file is changed, i.e. attributes changed or written
* to, KDirWatch will emit the signal dirty().
*
* The implementation uses the QFileSystemWatch functionality which may
* uses platform dependant notification API and as last resort
* stat-based poller.
* The watched directories or files do not have to exist. When a watched
* directory is changed, i.e. when files therein are created or deleted,
* KDirWatch will emit the signal dirty(). Scanning begins immediately when a
* dir or file watch is added.
*
* @see self()
* @author Sven Radej (in 1998)
*/
class KDECORE_EXPORT KDirWatch : public QObject
{
Q_OBJECT
public:
/**
* Available watch modes for directory monitoring
**/
enum WatchMode {
WatchDirOnly = 0, ///< Watch just the specified directory
WatchFiles = 0x01, ///< Watch also all files contained by the directory
WatchSubDirs = 0x02 ///< Watch also all the subdirs contained by the directory
};
Q_DECLARE_FLAGS(WatchModes, WatchMode)
Q_OBJECT
public:
/**
* Constructor.
*
* Scanning begins immediately when a dir/file watch
* is added.
* @param parent the parent of the QObject (or 0 for parent-less KDirWatch)
*/
KDirWatch(QObject* parent = 0);
@ -91,54 +55,34 @@ class KDECORE_EXPORT KDirWatch : public QObject
/**
* Adds a directory to be watched.
*
* The directory does not have to exist. When @p watchModes is set to
* WatchDirOnly (the default), the signals dirty(), created(), deleted()
* can be emitted, all for the watched directory.
* When @p watchModes is set to WatchFiles, all files in the watched
* directory are watched for changes, too. Thus, the signals dirty(),
* created(), deleted() can be emitted.
* When @p watchModes is set to WatchSubDirs, all subdirs are watched using
* the same flags specified in @p watchModes (symlinks aren't followed).
* If the @p path points to a symlink to a directory, the target directory
* is watched instead. If you want to watch the link, use @p addFile().
*
* @param path the path to watch
* @param watchModes watch modes
*
* @sa KDirWatch::WatchMode
*/
void addDir(const QString& path, WatchModes watchModes = WatchDirOnly);
void addDir(const QString &path);
/**
* Adds a file to be watched.
* If it's a symlink to a directory, it watches the symlink itself.
* @param file the file to watch
*/
void addFile(const QString& file);
void addFile(const QString &file);
/**
* Removes a directory from the list of scanned directories.
*
* If specified path is not in the list this does nothing.
* @param path the path of the dir to be removed from the list
*/
void removeDir(const QString& path);
void removeDir(const QString &path);
/**
* Removes a file from the list of watched files.
*
* If specified path is not in the list this does nothing.
* @param file the file to be removed from the list
*/
void removeFile(const QString& file);
void removeFile(const QString &file);
/**
* Check if a directory is being watched by this KDirWatch instance
* @param path the directory to check
* @return true if the directory is being watched
*/
bool contains( const QString& path) const;
bool contains(const QString &path) const;
/**
* The KDirWatch instance usually globally used in an application.
@ -155,63 +99,26 @@ class KDECORE_EXPORT KDirWatch : public QObject
static KDirWatch* self();
public Q_SLOTS:
/**
* Emits created().
* @param path the path of the file or directory
*/
void setCreated( const QString &path );
/**
* Emits dirty().
* @param path the path of the file or directory
*/
void setDirty( const QString &path );
/**
* Emits deleted().
* @param path the path of the file or directory
*/
void setDeleted( const QString &path );
Q_SIGNALS:
void setDirty(const QString &path);
Q_SIGNALS:
/**
* Emitted when a watched object is changed.
* For a directory this signal is emitted when files
* therein are created or deleted.
* For a file this signal is emitted when its size or attributes change.
*
* When you watch a directory, changes in the size or attributes of
* contained files may or may not trigger this signal to be emitted
* depending on which backend is used by KDirWatch.
* For a directory this signal is emitted when files therein are created or
* deleted. For a file this signal is emitted when its size or attributes
* change.
*
* The new ctime is set before the signal is emitted.
* @param path the path of the file or directory
*/
void dirty(const QString &path);
/**
* Emitted when a file or directory is created.
* @param path the path of the file or directory
*/
void created(const QString &path);
/**
* Emitted when a file or directory is deleted.
*
* The object is still watched for new creation.
* @param path the path of the file or directory
*/
void deleted(const QString &path);
private:
private:
KDirWatchPrivate* d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KDirWatch::WatchModes)
#endif
// vim: sw=3 et
#endif // KDIRWATCH_H

View file

@ -1,38 +1,29 @@
/* Private Header for class of KDirWatchPrivate
*
* this separate header file is needed for MOC processing
* because KDirWatchPrivate has signals and slots
*
* This file is part of the KDE libraries
* Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
* Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
* Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
* Copyright (C) 2008 Jarosław Staniek <staniek@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* This file is part of the KDE libraries
Copyright (C) 2019 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDIRWATCH_P_H
#define KDIRWATCH_P_H
#include "kdirwatch.h"
#include <QtCore/qfilesystemwatcher.h>
/* KDirWatchPrivate is a singleton and does the watching
* for every KDirWatch instance in the application.
*/
#include <QFileSystemWatcher>
#include <QStringList>
class KDirWatchPrivate
{
public:
@ -41,7 +32,8 @@ public:
public:
QFileSystemWatcher *watcher;
QStringList watchedfiles;
QStringList watcheddirs;
};
#endif // KDIRWATCH_P_H

View file

@ -1,72 +0,0 @@
/* This file is part of the KDE libraries
Copyright 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdirwatchtest.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <kdebug.h>
#include <kcmdlineargs.h>
int main (int argc, char **argv)
{
KCmdLineOptions options;
options.add("+[directory ...]", ki18n("Directory(ies) to watch"));
KCmdLineArgs::init(argc, argv, "kdirwatchtest", 0, ki18n("KDirWatchTest"),
"1.0", ki18n("Test for KDirWatch"));
KCmdLineArgs::addCmdLineOptions( options );
KCmdLineArgs::addStdCmdLineOptions();
QCoreApplication a(argc, argv);
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
myTest testObject;
KDirWatch *dirwatch1 = KDirWatch::self();
KDirWatch *dirwatch2 = new KDirWatch;
testObject.connect(dirwatch1, SIGNAL(dirty(QString)), SLOT(dirty(QString)) );
testObject.connect(dirwatch1, SIGNAL(created(QString)), SLOT(created(QString)) );
testObject.connect(dirwatch1, SIGNAL(deleted(QString)), SLOT(deleted(QString)) );
if (args->count() >0) {
for(int i = 0; i < args->count(); i++) {
kDebug() << "Watching: " << args->arg(i);
dirwatch2->addDir( args->arg(i));
}
}
QString home = QString(getenv ("HOME")) + '/';
QString desk = home + "Desktop/";
kDebug() << "Watching: " << home;
dirwatch1->addDir(home);
kDebug() << "Watching file: " << home << "foo ";
dirwatch1->addFile(home+"foo");
kDebug() << "Watching: " << desk;
dirwatch1->addDir(desk);
QString test = home + "test/";
kDebug() << "Watching: (but skipped) " << test;
dirwatch1->addDir(test);
delete dirwatch2;
return a.exec();
}
#include "moc_kdirwatchtest.cpp"

View file

@ -460,7 +460,7 @@ void Kded::updateDirWatch()
if (m_pDirWatch->contains(path)) {
continue;
}
m_pDirWatch->addDir(path, KDirWatch::WatchFiles|KDirWatch::WatchSubDirs);
m_pDirWatch->addDir(path);
}
}