From ce402f298b2f9733b614fbf1bde99a052d0ab5c0 Mon Sep 17 00:00:00 2001 From: Arno Date: Thu, 21 Mar 2013 16:14:54 +0100 Subject: Final inotify! A huge commit, I know, but it was definitely worth it! It makes the homebrew FilesystemModel work! It's divided up into two threads: 1. The Watcher: it reacts on inotify events and dispatches them to: 2. The Collector: this thread gathers the data and emits the SIGNALS to the the view. Now we can actually refresh the View, not possible with QFileSystemModel, and the data reloads in almost real time without blocking the GUI. Unfortunately this uncovered some bugs I had to fix: 1. Helper::md5sum: Don't crash if read fails with -1 2. SmTreeModel::addRow is broken. Even after 5h I couldn't figure out how or why, so I brute forced it. Reset the moded when a file is added. 3. Get rid of a lot of unneeded #include's I guess that's about it... --- filesystemfileproxy.cpp | 7 ++-- filesystemwidget.cpp | 2 +- helper.cpp | 10 +++-- smdirmodel.cpp | 51 +++++++++++++++-------- smdirmodel.h | 16 +++----- smdirwatcher.cpp | 107 +++++++++++++++++++++++++----------------------- smdirwatcher.h | 30 ++++++++------ smtreemodel.cpp | 6 +-- 8 files changed, 127 insertions(+), 102 deletions(-) diff --git a/filesystemfileproxy.cpp b/filesystemfileproxy.cpp index b45b740..249eeb4 100644 --- a/filesystemfileproxy.cpp +++ b/filesystemfileproxy.cpp @@ -11,8 +11,7 @@ #include #include #include - -#include +#include #include "filesystemfileproxy.h" #include "smdirmodel.h" @@ -21,9 +20,9 @@ FilesystemFileProxy::FilesystemFileProxy(QObject *parent) : QSortFilterProxyModel(parent) {} QVariant FilesystemFileProxy::data(const QModelIndex &index, int role) const{ - if(!index.isValid()){ + if(!index.isValid()){ return QVariant(); - } + } SmDirModel *source = qobject_cast(sourceModel()); if(role == Qt::DisplayRole){ QModelIndex real = mapToSource(index); diff --git a/filesystemwidget.cpp b/filesystemwidget.cpp index b842541..5f462e8 100644 --- a/filesystemwidget.cpp +++ b/filesystemwidget.cpp @@ -65,7 +65,7 @@ FilesystemWidget::FilesystemWidget(QWidget *parent) : QWidget(parent), mClipboar mFileProxy = new FilesystemFileProxy; mFileProxy->setSourceModel(mFileModel); - mFileView->setModel(mFileProxy); + mFileView->setModel(mFileProxy); mFileView->setSortingEnabled(true); mFileView->sortByColumn(0, Qt::AscendingOrder); mFileView->setItemsExpandable(false); diff --git a/helper.cpp b/helper.cpp index cfb1711..270feb1 100644 --- a/helper.cpp +++ b/helper.cpp @@ -68,7 +68,9 @@ namespace Helper { QByteArray data(4096, '\0'); do { read = file.read(data.data(), 4096); - h.addData(data.data(), read); + if(read > 0){ + h.addData(data.data(), read); + } } while (read == 4096); QByteArray res = h.result(); retval = res.toHex().toLower(); @@ -214,7 +216,7 @@ namespace Helper { QSettings s; QString ffProbe = s.value("paths/ffprobe").toString(); QStringList args; - QList retval; + QList retval = QList() << QVariant() << QVariant(); args << "-show_streams" << path; QProcess ffproc; ffproc.start(ffProbe, args); @@ -229,9 +231,9 @@ namespace Helper { QString llc = l.toLower(); if(llc.contains("duration")){ int idx = llc.indexOf("duration:"); - retval << llc.mid(idx + 10, 8); + retval[0] = llc.mid(idx + 10, 8); idx = llc.indexOf("bitrate:"); - retval << llc.mid(idx + 9); + retval[1] = llc.mid(idx + 9); break; } } diff --git a/smdirmodel.cpp b/smdirmodel.cpp index 914acff..d24fdef 100644 --- a/smdirmodel.cpp +++ b/smdirmodel.cpp @@ -5,14 +5,9 @@ 2 of the License, or (at your option) any later version. */ -#include #include -#include #include #include -#include - -#include #include "smdirmodel.h" #include "smdirwatcher.h" @@ -21,10 +16,7 @@ #include "helper.h" SmDirModel::SmDirModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent), mHeaders(headers){ - mCollector = new SmDataGatherer(NumFields, this); - mWatch = new SmDirWatcher(this); - connect(mWatch, SIGNAL(dwEvent(QString,int)), this, SLOT(dirEvent(QString,int))); connect(mWatch, SIGNAL(needRefresh()), this, SLOT(refresh())); mRunTimer = new QTimer(this); mRunTimer->setInterval(2000); @@ -32,7 +24,9 @@ SmDirModel::SmDirModel(const QStringList &headers, QObject *parent) : SmTreeMode mRunTimer->start(); readSettings(); + mCollector = mWatch->collector(); connect(mCollector, SIGNAL(population(SmTreeItem*)), this, SLOT(populate(SmTreeItem*))); + connect(mCollector, SIGNAL(newData(QList,int)), this, SLOT(dirEvent(QList,int)), Qt::BlockingQueuedConnection); } SmDirModel::~SmDirModel(){ @@ -84,6 +78,7 @@ bool SmDirModel::setData(const QModelIndex &index, const QVariant &value, int ro return false; } if(role == Qt::EditRole && index.column() == Name){ + //this is a rename QString newName = value.toString(); if(newName.contains(QDir::separator())){ return false; @@ -94,6 +89,8 @@ bool SmDirModel::setData(const QModelIndex &index, const QVariant &value, int ro QString newPath = QString("%1/%2").arg(dir).arg(newName); QFile::rename(old, newPath); emit needResize(); + // watcher->collector will do the rest + return true; } return SmTreeModel::setData(index, value, role); } @@ -107,6 +104,10 @@ bool SmDirModel::isDir(const QModelIndex &idx) const { return fi.isDir(); } +QDir SmDirModel::dir() const{ + return QDir(mCurrentDir); +} + QFileInfo SmDirModel::fileInfo(const QModelIndex &idx) const { if(!idx.isValid()){ return QFileInfo(); @@ -117,25 +118,26 @@ QFileInfo SmDirModel::fileInfo(const QModelIndex &idx) const { void SmDirModel::setDir(const QString &dir){ mCurrentDir = dir; - mCollector->setCurrent(mCurrentDir); mCollector->start(); mWatch->setDir(mCurrentDir); } -void SmDirModel::dirEvent(const QString &file, int e){ - QFileInfo fi(file); - const QList fData = fileData(fi); +void SmDirModel::dirEvent(const QList &data, int e){ if(e == SmDirWatcher::Added){ - addRow(fData, rootIndex(), true); + /* for some reason SmTreeModel::addRow() doesn't work, + * couldn't figure it out in 5 hours, so customize it + * and reset the model + */ + addFile(data); } - QModelIndex idx = find(fData.at(Name), Name, rootIndex()); + QModelIndex idx = find(data.at(Name), Name, rootIndex()); if(e == SmDirWatcher::Deleted){ removeRow(idx.row()); } if(e == SmDirWatcher::Modified){ for(int i = 0; i < mHeaders.count(); ++i){ QModelIndex c = index(idx.row(), i, QModelIndex()); - setData(c, fData.at(i), Qt::EditRole); + setData(c, data.at(i), Qt::EditRole); } } emit needResize(); @@ -155,8 +157,6 @@ void SmDirModel::readSettings(){ } void SmDirModel::refresh(){ - mCollector->setCurrent(mCurrentDir); - mCollector->start(); } void SmDirModel::populate(SmTreeItem *root){ @@ -164,6 +164,23 @@ void SmDirModel::populate(SmTreeItem *root){ emit needResize(); } +void SmDirModel::addFile(const QList &data){ + SmTreeItem *newItem = new SmTreeItem(data, root()); + int w = root()->childCount(); + int i = 0; + while(i < root()->childCount()){ + if(newItem->data(Name).toString().toLower() < root()->child(i)->data(Name).toString().toLower()){ + w = i; + break; + } + ++i; + } + beginResetModel(); + root()->insertChild(w, newItem); + endResetModel(); + return; +} + const QList SmDirModel::fileData(const QFileInfo &fi) const{ QList data; data << fi.fileName() << fi.size(); diff --git a/smdirmodel.h b/smdirmodel.h index c8cc69c..2db790b 100644 --- a/smdirmodel.h +++ b/smdirmodel.h @@ -8,13 +8,6 @@ #ifndef SMDIRMODEL_H #define SMDIRMODEL_H -#include -#include -#include -#include -#include -#include -#include #include #include @@ -24,7 +17,7 @@ class SmDirWatcher; class QTimer; class SmTreeItem; class QMutex; -class SmDataGatherer; +class SmDataColletor; class SmDirModel : public SmTreeModel { Q_OBJECT @@ -38,16 +31,19 @@ class SmDirModel : public SmTreeModel { virtual QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); bool isDir(const QModelIndex &idx) const; + QDir dir() const; QFileInfo fileInfo(const QModelIndex &idx) const; + public slots: void setDir(const QString &dir); - void dirEvent(const QString &file, int e); + void dirEvent(const QList &data, int e); void readSettings(); void refresh(); private slots: void populate(SmTreeItem *root); + void addFile(const QList &data); signals: void needResize(); @@ -61,7 +57,7 @@ class SmDirModel : public SmTreeModel { QTimer *mRunTimer; QMap mIcons; QMutex *mCollectorMx; - SmDataGatherer *mCollector; + SmDataColletor *mCollector; }; #endif // SMDIRMODEL_H diff --git a/smdirwatcher.cpp b/smdirwatcher.cpp index 8650d3b..8b72aab 100644 --- a/smdirwatcher.cpp +++ b/smdirwatcher.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -23,17 +24,26 @@ extern int errno; SmDirWatcher::SmDirWatcher(QObject *parent) : QThread(parent), mFd(0), mDescr(0) { - mFd = inotify_init1(IN_NONBLOCK); + mBufLen = 1024 * (sizeof(struct inotify_event) + 16); + mINdata = new char[mBufLen]; + mFd = inotify_init(); + + mCollector = new SmDataColletor(8, this); + mSemFree = new QSemaphore(1024); + mSemUsed = new QSemaphore; + mDataQueue = new QQueue >(); + mCollector->init(mSemFree, mSemUsed, mDataQueue); + mCollector->start(); } SmDirWatcher::~SmDirWatcher(){ if(mFd && mDescr){ inotify_rm_watch(mFd, mDescr); } + delete mINdata; } void SmDirWatcher::setDir(const QString &dir){ - QMutexLocker lock(&mWatchMx); if(mDescr){ inotify_rm_watch(mFd, mDescr); //generates IN_IGNORE ??? } @@ -44,78 +54,73 @@ void SmDirWatcher::setDir(const QString &dir){ */ mDescr = inotify_add_watch(mFd, qPrintable(dir), IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO); mCurrent = dir; + mSemFree->acquire(); + QPair c = qMakePair(dir, Populate); + mDataQueue->enqueue(c); + mSemUsed->release(); } void SmDirWatcher::run(){ - QMutexLocker lock(&mWatchMx); - QVarLengthArray data(4096); - int r = read(mFd, data.data(), data.size()); - int err = errno; - if(r < 0){ - if(err != EAGAIN){ - return; - } - r = read(mFd, data.data(), data.size()); - } - // inspect data - char *at = data.data(); - inotify_event *e = reinterpret_cast(at); - if(!e->len){ + int r = read(mFd, mINdata, mBufLen); + if(r <= 0){ return; } - char *end = at + data.size(); - while(at < end){ - if(e->mask & IN_IGNORED){ - at += sizeof(inotify_event) + e->len; + int i = 0; + while(i < r){ + inotify_event *e = reinterpret_cast(&mINdata[i]); + if((!e->len) || (e->mask & IN_IGNORED)){ + i += sizeof(inotify_event) + e->len; continue; } + mSemFree->acquire(); quint32 mask = e->mask; QString name = QString("%1/%2").arg(mCurrent).arg(e->name); - at += sizeof(inotify_event) + e->len; - if(mask & IN_CREATE){ - emit dwEvent(name, Added); - return; + DWEvent curEvent = None; + if(mask & IN_CREATE || mask & IN_MOVED_TO){ + curEvent = Added; } - if(mask & IN_DELETE){ - emit dwEvent(name, Deleted); - return; + if(mask & IN_DELETE || mask & IN_MOVED_FROM){ + curEvent = Deleted; } if(mask & IN_CLOSE_WRITE || e->mask & IN_MODIFY){ - emit dwEvent(name, Modified); - return; - } - if(mask & IN_MOVED_FROM || e->mask & IN_MOVED_TO){ - emit needRefresh(); - return; + curEvent = Modified; } + QPair c = qMakePair(name, curEvent); + mDataQueue->enqueue(c); + mSemUsed->release(); + i += sizeof(inotify_event) + e->len; } } -SmDataGatherer::SmDataGatherer(const int numFields, QObject *parent) : QThread(parent), mNumFields(numFields) {} +SmDataColletor::SmDataColletor(const int numFields, QObject *parent) : QThread(parent), mSemFree(0), mSemUsed(0), mDataQueue(0), mNumFields(numFields) {} -void SmDataGatherer::setCurrent(const QString ¤t, int mode){ - QMutexLocker lock(&mSetMx); - mCurrent = current; - mMode = mode; +void SmDataColletor::init(QSemaphore *set, QSemaphore *get, QQueue > *data){ + mSemFree = set; + mSemUsed = get; + mDataQueue = data; } -void SmDataGatherer::run(){ - QMutexLocker lock(&mRunMx); - QFileInfo fi(mCurrent); - if(fi.isDir()){ - SmTreeItem *rv = populate(); - emit population(rv); - return; - } - if(fi.isFile()){ +void SmDataColletor::run(){ + forever { + mSemUsed->acquire(); + QPair cur = mDataQueue->dequeue(); + if(cur.second == SmDirWatcher::Populate){ + SmTreeItem *i = populate(cur.first); + emit population(i); + mSemFree->release(); + continue; + + } + QFileInfo fi(cur.first); QList fd = fileData(fi); - emit newData(fd, mMode); + emit newData(fd, cur.second); + mSemFree->release(); } } -SmTreeItem * SmDataGatherer::populate(){ +SmTreeItem * SmDataColletor::populate(const QString &dir){ SmTreeItem *retval = new SmTreeItem(mNumFields); - QDir d = QDir(mCurrent); + QDir d = QDir(dir); foreach(QFileInfo fi, d.entryInfoList()){ if(fi.fileName() == "."){ continue; @@ -127,7 +132,7 @@ SmTreeItem * SmDataGatherer::populate(){ return retval; } -const QList SmDataGatherer::fileData(const QFileInfo &fi) const{ +const QList SmDataColletor::fileData(const QFileInfo &fi) const{ QList data; data << fi.fileName() << fi.size(); QString mime = Helper::mimeType(fi.absoluteFilePath()); diff --git a/smdirwatcher.h b/smdirwatcher.h index 3167aeb..19cd746 100644 --- a/smdirwatcher.h +++ b/smdirwatcher.h @@ -9,18 +9,21 @@ #define SMDIRWATCHER_H #include -#include #include #include #include +#include class SmTreeItem; +class QSemaphore; +class SmDataColletor; class SmDirWatcher : public QThread { Q_OBJECT public: - enum DWEvent { Added, Deleted, Modified }; + enum DWEvent { None, Added, Deleted, Modified, Populate }; explicit SmDirWatcher(QObject *parent = 0); + SmDataColletor *collector() { return mCollector; } ~SmDirWatcher(); signals: @@ -34,20 +37,23 @@ class SmDirWatcher : public QThread { private: int mFd; int mDescr; - QMutex mWatchMx; QString mCurrent; + SmDataColletor *mCollector; + QSemaphore *mSemFree; + QSemaphore *mSemUsed; + QQueue > *mDataQueue; + char *mINdata; + int mBufLen; }; -class SmDataGatherer : public QThread { +class SmDataColletor : public QThread { Q_OBJECT public: - explicit SmDataGatherer(const int numFields, QObject *parent = 0); - void setCurrent(const QString ¤t, int mode = -1); + explicit SmDataColletor(const int numFields, QObject *parent = 0); + void init(QSemaphore *set, QSemaphore *get, QQueue > *data); public slots: void run(); - //void setFile(const QString &fullPath, int event); - //void populate(const QString &dir); signals: void newData(const QList,int); @@ -55,12 +61,12 @@ class SmDataGatherer : public QThread { void needRefresh(); private: - //QList > populate(); - SmTreeItem *populate(); + SmTreeItem *populate(const QString &dir); const QList fileData(const QFileInfo &fi) const; QString mCurrent; - QMutex mRunMx; - QMutex mSetMx; + QSemaphore *mSemFree; + QSemaphore *mSemUsed; + QQueue > *mDataQueue; int mMode; const int mNumFields; }; diff --git a/smtreemodel.cpp b/smtreemodel.cpp index c8a0452..4f8ffdc 100644 --- a/smtreemodel.cpp +++ b/smtreemodel.cpp @@ -266,10 +266,10 @@ bool SmTreeModel::addRow(const QList &data, const QModelIndex &parent, } QModelIndex start = index(parentItem->childCount() - 1, 0, parent); QModelIndex end = index(parentItem->childCount() - 1, parentItem->columnCount() - 1, parent); - emit dataChanged(start, end); + emit dataChanged(start, end); return true; - } - return false; + } + return false; } SmTreeItem *SmTreeModel::itemAt(const QModelIndex &index) const{ -- cgit v1.2.3-70-g09d2