Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

filewatcher.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "filewatcher.h"
#include <glibmm/main.h>
#include "warning.h"
#include "filesys.h"
#include <stdexcept>

#include "defines.h"
#ifdef HAVE_LIBFAM
#include <fam.h>

/// SGI File Alteration Monitor wrapper
namespace FAM {
  class FAM;

  struct Error: public std::runtime_error {
    Error(const std::string& msg) : std::runtime_error(msg) {}
  };

  /** Create a File object and listen to its signal for changes. */
  class File {
  public:
    /* \param _filename Must be an absolute path. */
    File(const std::string& _filename);
    ~File();

    /// Signal emitted every time a change is detected.
    SigC::Signal0<void> signal;

  private:
    std::string filename;
    FAMRequest fr;
  };

  /** A singleton with no public methods. Used only by File. */
  class Server: public SigC::Object {
    friend class File;
    static Server *_instance;
    bool ok;
    FAMConnection fc;
    int fd;
    static Server &instance() {
      if(!_instance)
      _instance = new Server();
      return *_instance;
    }
    Server() {
      ok = FAMOpen(&fc) == 0;
      if(!ok) {
      verbose << "Failed to contact FAM server" << std::endl;
      return;
      }
      verbose << "Contact with FAM server established" << std::endl;
      fd = FAMCONNECTION_GETFD(&fc);
      /// \todo don't poll
      Glib::signal_timeout().connect(slot(*this, &Server::poll), 
                             100);
    }
    ~Server() {
      if(ok)
      FAMClose(&fc); // might fail, but so what
    }
    bool poll() {
      int pending;
      while(pending = FAMPending(&fc)) {
      if(pending < 0)
        return true; /// \todo handle error somehow
      FAMEvent fe;
      if(FAMNextEvent(&fc, &fe) 
         && (fe.code == FAMChanged || fe.code == FAMDeleted)) {
        File *file = static_cast<File*>(fe.userdata);
        file->signal();
        debug << "modified (fam)" << std::endl;
      }
      }
      return true;
    }
  };

  Server *Server::_instance = 0;

  File::File(const std::string& _filename) :filename(_filename) {
    debug << filename << " ..." << std::endl;
    if(filename.empty())
      return;
    if(!Server::instance().ok)
      throw Error("No contact with fam");
    if(FAMMonitorFile(&Server::instance().fc, filename.c_str(), &fr, this) < 0)
      throw Error("Could not monitor file \"" + filename + '"');
    debug << filename << " on" << std::endl;
  }

  File::~File() {
    debug << filename << " off" << std::endl;
    if(!filename.empty() && Server::instance().ok
       && (FAMCancelMonitor(&Server::instance().fc, &fr) != 0))
      warning << "Failed to stop watching \"" + filename + '"';
  }
}

#else
// dummy object to keep ifdefs out of the header
namespace FAM { class File {}; }
#endif

using namespace std;

00110 FileWatcher::FileWatcher(const std::string &filename) {
  set_file(filename);
}

FileWatcher::~FileWatcher() {}

void FileWatcher::set_file(const std::string &filename) {
  file = filename;
#ifdef HAVE_LIBFAM
  try {
    famfile.reset(new FAM::File(file));
    famfile->signal.connect(modified_signal.slot());
  }
  catch(const std::exception&) { famfile.reset(); }
#endif
  if(!famfile.get()) { // fam is not enabled or doesn't work
    try { last_time = modified(file); } catch(const std::exception&) {}
    if(!connection.connected()) {
      debug << "Connecting to timeout" << std::endl;
      connection = 
      Glib::signal_timeout().connect(slot(*this, &FileWatcher::check_file),
                               100);
    }
  } else connection.disconnect();
}

bool FileWatcher::check_file() {
  try {
    time_t new_time = modified(file);
    if(new_time != last_time) {
      last_time = new_time;
      modified_signal();
      debug << "modified" << std::endl;
    }
  }
  catch(const std::exception&) {}

  return true;   // don't disconnect
}

Generated by  Doxygen 1.6.0   Back to index