-rw-r--r-- | examples/opiecore/onotifydemo/onotifydemo.cpp | 14 | ||||
-rw-r--r-- | examples/opiecore/onotifydemo/onotifydemo.h | 2 | ||||
-rw-r--r-- | libopie2/opiecore/ofilenotify.cpp | 122 | ||||
-rw-r--r-- | libopie2/opiecore/ofilenotify.h | 63 |
4 files changed, 163 insertions, 38 deletions
diff --git a/examples/opiecore/onotifydemo/onotifydemo.cpp b/examples/opiecore/onotifydemo/onotifydemo.cpp index 2beda2a..e147c6a 100644 --- a/examples/opiecore/onotifydemo/onotifydemo.cpp +++ b/examples/opiecore/onotifydemo/onotifydemo.cpp @@ -79,100 +79,102 @@ DemoApp::DemoApp( int argc, char** argv ) : OApplication( argc, argv, "libopie2 showMainWidget( vbox ); } void DemoApp::addTrigger( bool multi ) { if ( !m ) { QMessageBox::warning( 0, "Add Trigger", "<p>Can't add trigger without at least one selected trigger type</p>", "&Sorry", 0 ); return; } QString filename = OFileDialog::getOpenFileName( OFileSelector::ExtendedAll ); if ( !filename.isEmpty() ) { bool success = true; odebug << "Filename = " << filename << oendl; int fntype = m; QString modifier = QString().sprintf( " = 0x%08x", fntype ); if ( QFileInfo( filename ).isFile() ) { if ( !multi ) { - success = OFileNotification::singleShot( filename, this, SLOT( unnamedTrigger() ), (OFileNotificationType) fntype ); + success = OFileNotification::singleShot( filename, this, SLOT(unnamedTrigger()), (OFileNotificationType) fntype ); } else { OFileNotification* fn = new OFileNotification(); success = fn->watch( filename, false, (OFileNotificationType) fntype ); - connect( fn, SIGNAL( triggered( const QString& ) ), this, SLOT( namedTrigger( const QString& ) ) ); - } + connect( fn, SIGNAL(triggered(const QString&,unsigned int,const QString&)), + this, SLOT(namedTrigger(const QString&,unsigned int,const QString&)) ); + } } else if ( QFileInfo( filename ).isDir() ) { ODirNotification* dn = new ODirNotification(); success = dn->watch( filename, !multi, (OFileNotificationType) fntype ); - connect( dn, SIGNAL( triggered( const QString& ) ), this, SLOT( namedTrigger( const QString& ) ) ); + connect( dn, SIGNAL(triggered(const QString&,unsigned int,const QString&)), + this, SLOT(namedTrigger(const QString&,unsigned int,const QString&)) ); } else { odebug << "Huh!? Neither file nor directory..." << oendl; return; } /* if ( !success ) { QMessageBox::warning( 0, "Add Trigger", "<p>Couldn't add trigger :(</p>", "&Sorry", 0 ); return; } else */ { new OListViewItem( l, filename, multi ? "MULTI" : "SINGLE", modifier ); } return; } else { odebug << "cancelled." << oendl; } } void DemoApp::modifierClicked( int modifier ) { m = static_cast<OFileNotificationType>( (int)m ^ int(modifier) ); }; void DemoApp::addSingle() { addTrigger(); }; void DemoApp::addMulti() { addTrigger( true ); }; void DemoApp::delTrigger() { QListViewItem* item = l->selectedItem(); if ( !item ) { QMessageBox::warning( 0, "Del Trigger", "<p>No trigger selected!</p>", "&Sorry", 0 ); return; } else { QString filename( item->text( 0 ) ); odebug << "Filename = " << filename << oendl; } } void DemoApp::unnamedTrigger() { owarn << "DemoApp::singleShotStrigger() : F I R E !!!!!" << oendl; } - void DemoApp::namedTrigger( const QString& path ) + void DemoApp::namedTrigger( const QString& path, unsigned int type, const QString& name ) { - owarn << "DemoApp::named trigger = " << path << " : F I R E !!!!!" << oendl; + owarn << "DemoApp::named trigger = ( " << path << ", " << type << ", " << name << " ) : F I R E !!!!!" << oendl; } int main( int argc, char** argv ) { DemoApp* app = new DemoApp( argc, argv ); app->exec(); return 0; } diff --git a/examples/opiecore/onotifydemo/onotifydemo.h b/examples/opiecore/onotifydemo/onotifydemo.h index f6ac5ea..20019e4 100644 --- a/examples/opiecore/onotifydemo/onotifydemo.h +++ b/examples/opiecore/onotifydemo/onotifydemo.h @@ -1,30 +1,30 @@ /* OPIE */ #include <opie2/olistview.h> #include <opie2/odebug.h> #include <opie2/oapplication.h> #include <opie2/ofilenotify.h> class QButtonGroup; class DemoApp : public Opie::Core::OApplication { Q_OBJECT public: DemoApp( int argc, char** argv ); public: void addTrigger( bool multi = false ); public slots: void modifierClicked( int modifier ); void addSingle(); void addMulti(); void delTrigger(); void unnamedTrigger(); - void namedTrigger( const QString& name ); + void namedTrigger( const QString&, unsigned int, const QString& ); private: Opie::Ui::OListView* l; QButtonGroup* g1; QButtonGroup* g2; Opie::Core::OFileNotificationType m; }; diff --git a/libopie2/opiecore/ofilenotify.cpp b/libopie2/opiecore/ofilenotify.cpp index 11d4f87..4264327 100644 --- a/libopie2/opiecore/ofilenotify.cpp +++ b/libopie2/opiecore/ofilenotify.cpp @@ -37,48 +37,66 @@ using namespace Opie::Core; #include <qsignal.h> #include <qintdict.h> #include <qdir.h> /* STD */ #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <assert.h> #include <string.h> #include <errno.h> #include <unistd.h> static QIntDict<OFileNotification> notification_list; QSocketNotifier* OFileNotification::_sn; int OFileNotification::_fd = -1; #define INOTIFY_DEVICE "/dev/inotify" namespace Opie { namespace Core { +//================================================================================================= +// OFileNotificationEvent +//================================================================================================= +OFileNotificationEvent::OFileNotificationEvent( OFileNotification* parent, int wd, unsigned int mask, unsigned int cookie, const QString& name ) + :_parent( parent ), _wd( wd ), _mask( mask ), _cookie( cookie ), _name( name ) +{ + qDebug( "OFileNotificationEvent()" ); +} + + +OFileNotificationEvent::~OFileNotificationEvent() +{ + qDebug( "~OFileNotificationEvent()" ); +} + +//================================================================================================= +// OFileNotification +//================================================================================================= OFileNotification::OFileNotification( QObject* parent, const char* name ) :QObject( parent, name ), _active( false ), _multi( true ) { qDebug( "OFileNotification::OFileNotification()" ); } OFileNotification::~OFileNotification() { stop(); qDebug( "OFileNotification::~OFileNotification()" ); } bool OFileNotification::isActive() const { return _active; } int OFileNotification::watch( const QString& path, bool sshot, OFileNotificationType type ) { // check if path exists and is a regular file struct stat s; @@ -131,104 +149,121 @@ void OFileNotification::stop() { notification_list.remove( _wd ); _path = QString::null; _wd = 0; _active = false; if ( notification_list.isEmpty() ) { OFileNotification::unregisterEventHandler(); } } OFileNotificationType OFileNotification::type() const { return _type; } QString OFileNotification::path() const { return _path; } -bool OFileNotification::activate() +bool OFileNotification::activate( const OFileNotificationEvent* e ) { - emit triggered( _path ); + qDebug( "OFileNotification::activate(): e = ( %s, %d, 0x%08x, %d, %s )", (const char*) _path, e->descriptor(), e->mask(), e->cookie(), (const char*) e->name() ); + + // dumb signal _signal.activate(); + + // generic signal + emit triggered( _path, e->mask(), e->name() ); + + // specialized signals + switch ( e->mask() ) + { + case Access: emit accessed( _path ); break; + case Modify: emit modified( _path ); break; + case Attrib: emit attributed( _path); break; + case CloseWrite: emit closed( _path, true ); break; + case CloseNoWrite: emit closed( _path, false ); break; + case Open: emit opened( _path ); break; + case MovedFrom: emit movedFrom( _path, e->name() ); break; + case MovedTo: emit movedTo( _path, e->name() ); break; + case DeleteSubdir: emit deletedSubdir( _path, e->name() ); break; + case DeleteFile: emit deletedFile( _path, e->name() ); break; + case CreateSubdir: emit createdSubdir( _path, e->name() ); break; + case CreateFile: emit createdFile( _path, e->name() ); break; + case DeleteSelf: emit deleted( _path ); break; + case Unmount: emit unmounted( _path ); break; + default: assert( 0 ); + } + if ( !_multi ) stop(); + return true; } bool OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) { OFileNotification* ofn = new OFileNotification(); ofn->_signal.connect( receiver, member ); return ofn->watch( path, true, type ) != -1; } void OFileNotification::inotifyEventHandler() { - qWarning( "OFileNotification::__eventHandler(): reached." ); + qDebug( "OFileNotification::inotifyEventHandler(): reached." ); char buffer[16384]; size_t buffer_i; struct inotify_event *pevent, *event; ssize_t r; size_t event_size; int count = 0; r = ::read(_fd, buffer, 16384); if ( r <= 0 ) return; buffer_i = 0; while ( buffer_i < r ) { - /* Parse events and queue them ! */ - pevent = (struct inotify_event *)&buffer[buffer_i]; - event_size = sizeof(struct inotify_event) + pevent->len; - qDebug( "pevent->len = %d\n", pevent->len); - - OFileNotification* fn = notification_list[ pevent->wd ]; - if ( fn ) - fn->activate(); - else - assert( false ); - - //event = malloc(event_size); - //memmove(event, pevent, event_size); - //queue_enqueue(event, q); - buffer_i += event_size; - count++; + pevent = (struct inotify_event *)&buffer[buffer_i]; + event_size = sizeof(struct inotify_event) + pevent->len; + OFileNotificationEvent* e = new OFileNotificationEvent( notification_list[ pevent->wd ], pevent->wd, pevent->mask, + pevent->cookie, pevent->len ? pevent->name : 0 ); + e->activate(); + buffer_i += event_size; + count++; } - qDebug( "received %d events...", count ); - return; + qDebug( "OFileNotification::inotifyEventHandler(): processed %d events", count ); } bool OFileNotification::registerEventHandler() { OFileNotification::_fd = ::open( INOTIFY_DEVICE, O_RDONLY ); if ( OFileNotification::_fd < 0 ) { qWarning( "OFileNotification::registerEventHandler(): couldn't register event handler: %s", strerror( errno ) ); return false; } OFileNotification::_sn = new QSocketNotifier( _fd, QSocketNotifier::Read, this, "inotify event" ); connect( OFileNotification::_sn, SIGNAL( activated(int) ), this, SLOT( inotifyEventHandler() ) ); qDebug( "OFileNotification::registerEventHandler(): done" ); return true; } void OFileNotification::unregisterEventHandler() { if ( _sn ) delete _sn; if ( OFileNotification::_fd ) @@ -241,38 +276,75 @@ void OFileNotification::unregisterEventHandler() //================================================================================================= ODirNotification::ODirNotification( QObject* parent, const char* name ) :QObject( parent, name ) { qDebug( "ODirNotification::ODirNotification()" ); } ODirNotification::~ODirNotification() { qDebug( "ODirNotification::~ODirNotification()" ); } int ODirNotification::watch( const QString& path, bool sshot, OFileNotificationType type, int recurse ) { qDebug( "ODirNotification::watch( %s, %d, 0x%08x, %d )", (const char*) path, sshot, type, recurse ); if ( recurse == 0 ) { OFileNotification* fn = new OFileNotification( this, "ODirNotification delegate" ); int result = fn->startWatching( path, sshot, type ); if ( result != -1 ) { - connect( fn, SIGNAL( triggered( const QString& ) ), this, SIGNAL( triggered( const QString& ) ) ); - return result; + connect( fn, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ), this, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ) ); + connect( fn, SIGNAL( accessed( const QString& ) ), this, SIGNAL( accessed( const QString& ) ) ); + connect( fn, SIGNAL( modified( const QString& ) ), this, SIGNAL( modified( const QString& ) ) ); + connect( fn, SIGNAL( attributed( const QString& ) ), this, SIGNAL( attributed( const QString& ) ) ); + connect( fn, SIGNAL( closed( const QString&, bool ) ), this, SIGNAL( closed( const QString&, bool ) ) ); + connect( fn, SIGNAL( opened( const QString& ) ), this, SIGNAL( opened( const QString& ) ) ); + connect( fn, SIGNAL( movedTo( const QString&, const QString& ) ), this, SIGNAL( movedTo( const QString&, const QString& ) ) ); + connect( fn, SIGNAL( movedFrom( const QString&, const QString& ) ), this, SIGNAL( movedFrom( const QString&, const QString& ) ) ); + connect( fn, SIGNAL( deletedSubdir( const QString&, const QString& ) ), this, SIGNAL( deletedSubdir( const QString&, const QString& ) ) ); + connect( fn, SIGNAL( deletedFile( const QString&, const QString& ) ), this, SIGNAL( deletedFile( const QString&, const QString& ) ) );; + connect( fn, SIGNAL( createdSubdir( const QString&, const QString& ) ), this, SIGNAL( createdSubdir( const QString&, const QString& ) ) ); + connect( fn, SIGNAL( createdFile( const QString&, const QString& ) ), this, SIGNAL( createdFile( const QString&, const QString& ) ) ); + connect( fn, SIGNAL( deleted( const QString& ) ), this, SIGNAL( deleted( const QString& ) ) ); + connect( fn, SIGNAL( unmounted( const QString& ) ), this, SIGNAL( unmounted( const QString& ) ) ); } + return result; } else { - qDebug( "ODirNotification::watch(), recursion not yet implemented... :)" ); - return -1; + + return 1; } } + +// void ODirNotification::subdirCreated( const QString& name ) + + +/* + Love-Trowbridge recursive directory scanning algorithm: + + Step 1. Start at initial directory foo. Add watch. + + Step 2. Setup handlers for watch created in Step 1. + Specifically, ensure that a directory created + in foo will result in a handled CREATE_SUBDIR + event. + + Step 3. Read the contents of foo. + + Step 4. For each subdirectory of foo read in step 3, repeat + step 1. + + Step 5. For any CREATE_SUBDIR event on bar, if a watch is + not yet created on bar, repeat step 1 on bar. +*/ + + } // namespace Ui } // namespace Opie diff --git a/libopie2/opiecore/ofilenotify.h b/libopie2/opiecore/ofilenotify.h index 3eb917e..5bbf421 100644 --- a/libopie2/opiecore/ofilenotify.h +++ b/libopie2/opiecore/ofilenotify.h @@ -21,48 +21,50 @@ _;:, .> :=|. This program is free software; you can -. .:....=;==+<; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef OFILENOTIFY_H #define OFILENOTIFY_H #if defined (__GNUC__) && (__GNUC__ < 3) #define _GNU_SOURCE #endif #include "linux_inotify.h" /* QT */ #include <qsocketnotifier.h> #include <qsignal.h> #include <qstring.h> namespace Opie { namespace Core { +class OFileNotificationEvent; + /*====================================================================================== * OFileNotificationType *======================================================================================*/ /** * @brief An enumerate for the different types of file notifications * * This enumerate provides a means to specify the type of events that you are interest in. * Valid values are: * <ul> * <li>Access: The file was accessed (read) * <li>Modify The file was modified (write,truncate) * <li>Attrib = The file had its attributes changed (chmod,chown,chgrp) * <li>CloseWrite = Writable file was closed * <li>CloseNoWrite = Unwritable file was closed * <li>Open = File was opened * <li>MovedFrom = File was moved from X * <li>MovedTo = File was moved to Y * <li>DeleteSubdir = Subdir was deleted * <li>DeleteFile = Subfile was deleted * <li>CreateSubdir = Subdir was created * <li>CreateFile = Subfile was created * <li>DeleteSelf = Self was deleted * <li>Unmount = The backing filesystem was unmounted @@ -147,92 +149,141 @@ class OFileNotification : public QObject **/ int watch( const QString& path, bool sshot = false, OFileNotificationType type = Modify ); /** * Stop watching for file events. **/ void stop(); /** * @returns the notification type as set by @ref start(). **/ OFileNotificationType type() const; /** * @returns the path to the file being watched by this instance. **/ QString path() const; /** * @returns if a file is currently being watched. **/ bool isActive() const; /** * @internal */ int startWatching( const QString& path, bool sshot = false, OFileNotificationType type = Modify ); signals: - /** - * This signal is emitted if an event happens of the specified type happens to the file being watched. - **/ - void triggered( const QString& name ); + void triggered( const QString&, unsigned int, const QString& ); + void accessed( const QString& ); + void modified( const QString& ); + void attributed( const QString& ); + void closed( const QString&, bool ); + void opened( const QString& ); + void movedTo( const QString&, const QString& ); + void movedFrom( const QString&, const QString& ); + void deletedSubdir( const QString&, const QString& ); + void deletedFile( const QString&, const QString& ); + void createdSubdir( const QString&, const QString& ); + void createdFile( const QString&, const QString& ); + void deleted( const QString& ); + void unmounted( const QString& ); protected: - bool activate(); + bool activate( const OFileNotificationEvent* e ); private slots: void inotifyEventHandler(); private: bool registerEventHandler(); void unregisterEventHandler(); QString _path; OFileNotificationType _type; QSignal _signal; bool _active; bool _multi; static QSocketNotifier* _sn; int _wd; // inotify watch descriptor static int _fd; // inotify device descriptor + + friend class OFileNotificationEvent; }; /*====================================================================================== * ODirNotification *======================================================================================*/ /** * @brief Represents a directory notification * * This class allows to watch for events happening to directories * It uses the OFileNotification class * * @see http://www.kernel.org/pub/linux/kernel/people/rml/inotify/ * * @author Michael 'Mickey' Lauer <mickey@vanille.de> * **/ class ODirNotification : public QObject { Q_OBJECT public: ODirNotification( QObject* parent = 0, const char* name = 0 ); ~ODirNotification(); /** * Starts to watch for @a type changes to @a path. Recurse @a recurse levels down the filesystem tree, * use 0 for no recursion and -1 for unlimited recursion. * Set @a sshot to True if you want to be notified only once. **/ int watch( const QString& path, bool sshot = false, OFileNotificationType type = Modify, int recurse = 0 ); signals: /** * This signal is emitted if an event happens of the specified type happens to the directory being watched. **/ - void triggered( const QString& name ); + void triggered( const QString&, unsigned int, const QString& ); + void accessed( const QString& ); + void modified( const QString& ); + void attributed( const QString& ); + void closed( const QString&, bool ); + void opened( const QString& ); + void movedTo( const QString&, const QString& ); + void movedFrom( const QString&, const QString& ); + void deletedSubdir( const QString&, const QString& ); + void deletedFile( const QString&, const QString& ); + void createdSubdir( const QString&, const QString& ); + void createdFile( const QString&, const QString& ); + void deleted( const QString& ); + void unmounted( const QString& ); +}; + +/*====================================================================================== + * OFileNotificationEvent + *======================================================================================*/ + +class OFileNotificationEvent +{ + public: + OFileNotificationEvent( OFileNotification* parent, int wd, unsigned int mask, unsigned int cookie, const QString& name ); + ~OFileNotificationEvent(); + OFileNotification* parent() const { return _parent; }; + int descriptor() const { return _wd; }; + unsigned int mask() const { return _mask; }; + unsigned int cookie() const { return _cookie; }; + QString name() const { return _name; }; + void activate() { _parent->activate( this ); }; + + private: + OFileNotification* _parent; + int _wd; + unsigned int _mask; + unsigned int _cookie; + QString _name; }; } } #endif |