监视I/O

Glib(gtkmm的底层库之一)有一个很好的特性是它具有帮你检查文件描述符中的数据的能力。这对于需要联网的应用程序十分的有用。使用以下方法来执行此操作:

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

第一个参数是你希望在第二个参数指定的文件描述符上发生指定事件(见第三个参数的描述)时被调用的槽。第三个参数可能是一下的一项或多项(若想是多项则对以下多个使用|,例如:Glib::IO_IN | Glib::IO_OUT | Glib::IO_PRI):

  • Glib::IO_IN - 当你提供的文件描述符有数据准备好被读取时调用你的方法。
  • Glib::IO_OUT - 当提供的文件描述符准备好被写入时调用你的方法。
  • Glib::IO_PRI - 当提供的文件描述符有紧急数据要读取时调用你的方法。
  • Glib::IO_ERR - 当提供的文件描述符上发生错误的时候调用你的方法。
  • Glib::IO_HUP - 挂起时(常见于管道和套接字的连接被断开)调用你的方法。

返回值是一个sigc::connection对象,你可以在其上调用disconnect()方法以停止对该文件描述符的监视。信号处理函数slot应声明如下:

bool input_callback(Glib::IOCondition condition);

参数condition如上所述。通常使用sigc::mem_fun()(通过一个对象的成员函数创建)或sigc::ptr_fun()(通过一个普通函数创建)函数创建槽。

下面是个示例。要使用该示例,只需要在终端上执行它即可;它不会创建一个窗口。它将在当前目录创建一个名为testfifo的具名管道。然后启动另一个shell并执行echo "Hello" > testfifo。该示例将打印你输入的每一行直到你执行echo "Q" > testfifo

源代码

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