/*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* glogg 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with glogg. If not, see .
*/
#ifndef WINWATCHTOWERDRIVER_H
#define WINWATCHTOWERDRIVER_H
#include
#include
#include
#include
#include
#include
#define WIN32_LEAN_AND_MEAN
#include
// Utility class
// Encapsulate a directory notification returned by Windows'
// ReadDirectoryChangesW.
class WinNotificationInfo {
public:
enum class Action { UNDEF,
ADDED,
REMOVED,
MODIFIED,
RENAMED_OLD_NAME,
RENAMED_NEW_NAME };
WinNotificationInfo() { action_ = Action::UNDEF; }
WinNotificationInfo( Action action, std::wstring file_name )
{ action_ = action; file_name_ = file_name; }
Action action() const { return action_; }
std::wstring fileName() const { return file_name_; }
private:
Action action_;
std::wstring file_name_;
};
class WinNotificationInfoList {
public:
WinNotificationInfoList( const char* buffer, size_t buffer_size );
// Iterator
class iterator : std::iterator {
public:
iterator( WinNotificationInfoList* list, const char* position )
{ list_ = list; position_ = position; }
const WinNotificationInfo& operator*() const
{ return list_->current_notification_; }
const WinNotificationInfo* operator->() const
{ return &( list_->current_notification_ ); }
const WinNotificationInfo& operator++() {
position_ = list_->advanceToNext();
return list_->current_notification_;
}
WinNotificationInfo operator++( int ) {
WinNotificationInfo tmp { list_->current_notification_ };
operator++();
return tmp;
}
bool operator!=( const iterator& other ) const {
return ( list_ != other.list_ ) || ( position_ != other.position_ );
}
private:
WinNotificationInfoList* list_;
const char* position_;
};
iterator begin() { return iterator( this, pointer_ ); }
iterator end() { return iterator( this, nullptr ); }
private:
const char* advanceToNext();
// Current notification (in the byte stream)
const char* pointer_;
// Next notification (in the byte stream)
const char* next_;
WinNotificationInfo current_notification_;
const char* updateCurrentNotification( const char* new_position );
};
template
class ObservedFile;
template
class ObservedFileList;
class WinWatchTowerDriver {
public:
struct WinWatchedDirRecord {
WinWatchedDirRecord( const std::string& file_name )
: path_( file_name ) { }
static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096;
std::string path_;
void* handle_ = nullptr;
static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE;
char buffer_[buffer_length_];
};
class FileId { };
class SymlinkId { };
class DirId {
public:
friend class WinWatchTowerDriver;
DirId() {}
bool operator==( const DirId& other ) const
{ return dir_record_ == other.dir_record_; }
bool valid() const
{ return ( dir_record_ != nullptr ); }
private:
std::shared_ptr dir_record_;
};
// On Windows, the token is the "last write" time
class FileChangeToken {
public:
FileChangeToken() {}
FileChangeToken( const std::string& file_name )
{ readFromFile( file_name ); }
void readFromFile( const std::string& file_name );
bool operator==( const FileChangeToken& other )
{ return ( low_date_time_ == other.low_date_time_ ) &&
( high_date_time_ == other.high_date_time_ ); }
bool operator!=( const FileChangeToken& other )
{ return ! operator==( other ); }
private:
DWORD low_date_time_ = 0;
DWORD high_date_time_ = 0;
};
// Default constructor
WinWatchTowerDriver();
~WinWatchTowerDriver();
FileId addFile( const std::string& file_name );
SymlinkId addSymlink( const std::string& file_name );
DirId addDir( const std::string& file_name );
void removeFile( const FileId& file_id );
void removeSymlink( const SymlinkId& symlink_id );
void removeDir( const DirId& dir_id );
std::vector*> waitAndProcessEvents(
ObservedFileList* list,
std::unique_lock* lock,
std::vector*>* files_needing_readding,
int timout_ms );
// Interrupt waitAndProcessEvents
void interruptWait();
private:
// An action which will be picked up by the worker thread.
class Action {
public:
Action( std::function function ) : function_ { function } {}
~Action() {}
void operator()() { function_(); }
private:
std::function function_;
};
// Action
std::mutex action_mutex_;
std::condition_variable action_done_cv_;
std::unique_ptr scheduled_action_ = nullptr;
// Win32 notification variables
HANDLE hCompPort_;
OVERLAPPED overlapped_;
unsigned long buffer_length_;
// List of directory records
// Accessed exclusively in the worker thread context
std::vector> dir_records_ { };
// Private member functions
void serialisedAddDir(
const std::string& file_name,
DirId& dir_id );
};
#endif