Fonctions de contrôle des entrées/sorties

Une des caractéristiques intéressantes de GLib (une des bibliothèques sous-jacentes à gtkmm) est la possibilité de demander la vérification des données d'un descripteur de fichier à votre intention. La chose est particulièrement utile pour les applications réseau. Pour cette vérification, vous utilisez la fonction membre :

sigc::connection Glib::SignalInput::connect(
             const sigc::slot<bool,Glib::IOCondition>& slot,
             int fd,
             Glib::IOCondition condition,
             int priority = Glib::PRIORITY_DEFAULT);

Le premier paramètre est un connecteur qui est sollicité quand survient, sur le descripteur de fichier précisé en second paramètre, l'événement indiqué en troisième paramètre. Ce dernier définit une valeur ou plusieurs (liées avec |) parmi les suivantes :

  • Glib::IO_IN - appelle la fonction membre quand il y a des données prêtes en lecture dans le descripteur de fichier.
  • Glib::IO_OUT - appelle la fonction membre quand le descripteur de fichier est prêt pour l'écriture.
  • Glib::IO_PRI - appelle la fonction membre quand le descripteur de fichier a des données à lire d'urgence.
  • Glib::IO_ERR - appelle la fonction membre quand une erreur est arrivée sur le descripteur de fichier.
  • Glib::IO_HUP - appelle la fonction membre quand il y a une interruption (la connexion a été interrompue généralement pour les tubes et les connecteurs).

La valeur de retour qui est du type sigc::connection peut être utilisée pour arrêter le contrôle sur ce descripteur de fichier avec la fonction membre disconnect(). Le gestionnaire de signal de slot doit être déclaré comme suit :

bool input_callback(Glib::IOCondition condition);

où le paramètre condition est tel que précisé ci-dessus. Comme d'habitude, le connecteur est créé avec sigc::mem_fun() (pour une fonction membre d'un objet) ou sigc::ptr_fun() (pour une fonction).

Un petit exemple suit. Pour vous servir de cet exemple, lancez-le à partir d'un terminal ; il ne crée pas de fenêtre. Il va créer un tube nommé testfifo dans le répertoire actuel. Puis, lancez un autre instance de terminal et saisissez echo "Hello" > testfifo. Le terminal de l'exemple affiche chaque ligne saisie dans l'autre instance jusqu'à ce que vous saisissiez echo "Q" > testfifo.

Code source

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include <build/config.h>
#include <gtkmm/main.h>
#include <fcntl.h>
#include <iostream>

#include <unistd.h> //The SUN Forte compiler puts F_OK here.

//The SUN Forte compiler needs these for mkfifo:
#include <sys/types.h>
#include <sys/stat.h>

int read_fd;
Glib::RefPtr<Glib::IOChannel> iochannel;

/*
  send to the fifo with:
  echo "Hello" > testfifo

  quit the program with:
  echo "Q" > testfifo
*/

// this will be our signal handler for read operations
// it will print out the message sent to the fifo
// and quit the program if the message was 'Q'.
bool MyCallback(Glib::IOCondition io_condition)
{

  if ((io_condition & Glib::IO_IN) == 0) {
    std::cerr << "Invalid fifo response" << std::endl;
  }
  else {
   Glib::ustring buf;

   iochannel->read_line(buf);
   std::cout << buf;
   if (buf == "Q\n")
       Gtk::Main::quit ();

  }
  return true;
}


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

  if (access("testfifo", F_OK) == -1) {
    // fifo doesn't exit - create it
    #ifdef HAVE_MKFIFO
    if (mkfifo("testfifo", 0666) != 0) {
      std::cerr << "error creating fifo" << std::endl;
      return -1;
    }
    #else
      std::cerr << "error creating fifo: This platform does not have mkfifo()"
          << std::endl;
    #endif //HAVE_MKFIFO
  }

  read_fd = open("testfifo", O_RDONLY);
  if (read_fd == -1)
  {
    std::cerr << "error opening fifo" << std::endl;
    return -1;
  }

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

  // Creates a iochannel from the file descriptor
  iochannel = Glib::IOChannel::create_from_fd(read_fd);

  // 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;
}