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::SignalIO::connect(const sigc::slot<bool(Glib::IOCondition)>& slot,
                                 Glib::PollFD::fd_t fd, Glib::IOCondition condition,
                                 int priority = Glib::PRIORITY_DEFAULT);

The first argument is a slot you wish to have called when 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:

  • 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);

where condition is 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).

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.

Source Code

File: main.cc (For use with gtkmm 4)

#include <gtkmm/application.h>
#include <glibmm/main.h>
#include <glibmm/iochannel.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>

Glib::RefPtr<Gtk::Application> app;

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::IOCondition::IO_IN) != Glib::IOCondition::IO_IN) {
    std::cerr << "Invalid fifo response" << std::endl;
  }
  else {
   Glib::ustring buf;

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

  }
  return true;
}


int main(int argc, char *argv[])
{
  app = Gtk::Application::create("org.gtkmm.example");

  if (access("testfifo", F_OK) == -1) {
    // fifo doesn't exist - create it
    #ifndef DONT_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 //DONT_HAVE_MKFIFO
  }

  // Although we will only read from the fifo, we open it in read/write mode.
  // Due to a peculiarity with the poll() system call, used deep down in glib,
  // this small program will use all available CPU time, if the fifo is opened
  // as O_RDONLY. See a discussion on the gtkmm-list, e.g.
  // https://mail.gnome.org/archives/gtkmm-list/2015-September/msg00034.html
  // and the link from there to stackoverflow.
  read_fd = open("testfifo", O_RDWR);
  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::IOCondition::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->hold(); // keep the application running without a window
  app->run(argc, argv);

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

  return 0;
}