Monitoring I/O

TODO: This is now in Glib, not Gdk. A nifty feature of GDK (one of the libraries underlying gtkmm) is the ability to have it check for data on a file descriptor for you. This is especially useful for networking applications. The following method is used to do this:

sigc::connection Glib::Main::SignalInput::connect(const SlotType& sd, int source,
                                    Glib::InputCondition condition);

The first argument is a slot (SlotType is a typedef to a sigc::slot<>) you wish to have called when then the specified event (see argument 3) occurs on the file descriptor you specify using argument two. Argument three may be one or more (using |) of:

The return value is a sigc::connection that may be used to stop monitoring this file descriptor using its disconnect method. The sd signal handler should be declared as follows:

void input_callback(int source, GdkInputCondition condition);

where source and condition are as specified above. As usual the slot is created with sigc::mem_fun() (for a member method of an object.), or sigc::ptr_fun (for a function).

A little (and somewhat dirty) example follows. To use the example just execute it from a terminal; it doesn't create a window. It will create a pipe named testpipe in the current directory. Then start another shell and execute cat >testpipe. The example will print each line you enter until you type quit.

Source Code

File: main.cc

#include <gtkmm/main.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fstream>
#include <iostream>
#include <memory>

std::auto_ptr<std::ifstream> input;


// this will be our signal handler for read operations
// there is not much to say. just read a string,
// print it and quit the application if the string was quit
bool MyCallback(Glib::IOCondition /* condition */)
{
  Glib::ustring dummy;

  do
  {
    (*input) >> dummy;
    std::cout << dummy << std::endl;
  
    if(dummy == "quit")
      Gtk::Main::quit();

  } while(input->fail());
  
  return true;
}


int main(int argc, char *argv[])
{
  // the usual Gtk::Main object
  Gtk::Main app(argc, argv);

  // create a fifo for testing purposes
  if (mkfifo("testfifo", 0666) != 0) {
    std::cerr << "error creating fifo" << std::endl;
    return -1;
  }

  //TODO: I have update this example to build with gtkmm 2, but I'm not sure which of the several commented-out 
  //ifstream and file descriptor things are needed, or should work.
  //We need to look at the gtkmm 1.2 version, if it worked.
  //Alternatively, just find a gtkmm app that monitors files for changes.
  //murrayc
  
  // open the fifo
  input = std::auto_ptr<std::ifstream>( new std::ifstream("testfifo") );
  
  int fd = open("testfifo", 0);
  if (fd == -1)
  {
    std::cerr << "error opening fifo" << std::endl;
    return -1;
  }

  // assign the fifo's filedescriptor to our ifstream object
  //This sucks; it will only ever work with libstdc++-v3, as
  //  both istream::__filebuf_type and the basic_filebuf contructor
  //  that takes an fd are libstdc++-v3 specific.
  //input = std::auto_ptr<std::ifstream>( new std::istream(new std::ifstream::__filebuf_type(fd, "testfifo")) );

  // connect the signal handler
  Glib::signal_io().connect(sigc::ptr_fun(MyCallback), fd, Glib::IO_IN);

  // and last but not least - run the application main loop
  app.run();

  // now remove the temporary fifo
  if(unlink("testfifo"))
    std::cerr << "error removing fifo" << std::endl;

  return 0;
}