Greffons et connecteurs

Aperçu
Parfois, il peut se révéler utile de pouvoir incorporer un élément graphique d'une autre application dans votre propre application. gtkmm permet cela grâce aux classes Gtk::Socket et Gtk::Plug. Il est peu vraisemblable que beaucoup d'applications aient besoin de cette fonctionnalité, mais dans les rares cas où vous auriez besoin d'afficher un élément graphique fonctionnant dans un processus tout à fait différent, ces classes peuvent être d'une grande aide.
La communication entre un objet Socket et un objet Plug s'effectue suivant le protocole XEmbed. Ce protocole a aussi été implémenté dans d'autres boîtes à outils (par exemple, Qt) ; cela permet un niveau d'intégration identique lorsqu'on incorpore un élément graphique Qt dans GTK+ ou vice versa.
Les Sockets et Plugs collaborent par l'intermédiaire des identifiants de leurs fenêtres. Un objet Socket et un objet Plug ont tous deux des ID que l'on peut retrouver avec leurs fonctions membres get_id(). L'utilisation de ces ID sera explicitée plus bas dans la Branchement des greffons et des connecteurs.

Connecteurs
Un objet Socket est une sorte d'élément graphique conteneur spécial qui autorise la possibilité d'incorporer des éléments graphiques d'un processus dans un autre de manière totalement transparente pour l'utilisateur.

Greffons
Un objet Plug est une variété de fenêtre particulière qu'il est possible de greffer dans un objet Socket. Outre les propriétés et les fonctions membres habituelles de la classe Gtk::Window, l'objet Plug fournit un constructeur qui prend comme paramètre l'ID d'un objet Socket ; l'objet Plug va automatiquement s'incorporer dans l'objet Socket qui a l'ID correspondant.
Comme un objet Plug n'est qu'un type spécial de classe Gtk::Window, vous pouvez lui ajouter des conteneurs ou des éléments graphiques exactement comme vous auriez fait pour une autre fenêtre.

Branchement des greffons et des connecteurs
After a Socket or Plug object is realized, you can obtain its ID with its get_id() function. This ID can then be shared with other processes so that other processes know how to connect to each other.
There are two basic strategies that can be used:
Create a Socket object in one process and pass the ID of that Socket to another process so that it can create a Plug object by specifying the given Socket ID in its constructor. There is no way to assign a Plug to a particular Socket after creation, so you must pass the Socket ID to the Plug's constructor.
Create a Plug independantly from any particular Socket and pass the ID of the Plug to other processes that need to use it. The ID of the Plug can be associated with a particular Socket object using the Socket::add_id() function. This is the approach used in the example below.

Exemple greffons et connecteurs
The following is a simple example of using sockets and plugs. The method of communication between processes is deliberately kept very simple: The Plug writes its ID out to a text file named plug.id and the process with the socket reads the ID from this file. In a real program, you may want to use a more sophisticated method of inter-process communication.
Source Code
File: plug.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include <fstream>
#include <gtkmm.h>
#include <gtkmm/plug.h>
#include <glib/gstdio.h>

using namespace std;

const char* id_filename = "plug.id";

void on_embed()
{
  cout << "I've been embedded." << endl;
}

class MyPlug : public Gtk::Plug
{
  public:
    MyPlug() :
      m_label("I am the plug")
  {
    set_size_request(150, 100);
    add(m_label);
    signal_embedded().connect(sigc::ptr_fun(on_embed));
    show_all_children();
  }

  private:
    Gtk::Label m_label;
};


int main(int argc, char** argv)
{
  // The plug and the socket have different application ids, so they can run
  // simultaneously.
  Glib::RefPtr<Gtk::Application> app =
    Gtk::Application::create(argc, argv, "org.gtkmm.example.plug");
  MyPlug plug;
  plug.show();

  ofstream out(id_filename);
  out << plug.get_id();
  out.close();
  cout << "The window ID is: " << plug.get_id() << endl;

  app->run(plug);

  // remove the ID file when the program exits
  g_remove(id_filename);
  return 0;
}

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

#include <iostream>
#include <fstream>
#include <gtkmm.h>
#include <gtkmm/socket.h>

using namespace std;

const char* id_filename = "plug.id";

void plug_added()
{
  cout << "A plug was added" << endl;
}

bool plug_removed()
{
  cout << "A Plug was removed" << endl;
  return true;
}

class MySocketWindow : public Gtk::Window
{
  public:
    MySocketWindow()
    {
      ifstream infile(id_filename);
      if (infile)
      {
        Gtk::Socket* socket = Gtk::manage(new Gtk::Socket());
        add(*socket);
        socket->signal_plug_added().connect(sigc::ptr_fun(plug_added));
        socket->signal_plug_removed().connect(sigc::ptr_fun(plug_removed));
        ::Window plug_id = 0;
        infile >> plug_id;
        infile.close();
        socket->add_id(plug_id);
      }
      else
      {
        Gtk::Label* label = Gtk::manage(
            new Gtk::Label(
              "Plug id file not found.\n Make sure plug is running."));
        add(*label);
        set_size_request(150, 50);
      }
      show_all();
    }
};

int main(int argc, char** argv)
{
  // The plug and the socket have different application ids, so they can run
  // simultaneously.
  Glib::RefPtr<Gtk::Application> app =
    Gtk::Application::create(argc, argv, "org.gtkmm.example.socket");
  MySocketWindow win;
  app->run(win);
  return 0;
}

Cet exemple crée deux exécutables : socket et plug. L'idée est que socket ait une fenêtre d'application qui accueille un élément graphique issu du programme plug. Compte tenu de la manière dont cet exemple est conçu, plug doit être lancé avant que socket ne démarre. Pour voir cet exemple en action, exécutez les commandes suivantes dans l'ordre indiqué à partir du répertoire de l'exemple :
Lancez le programme plug et envoyez-le en arrière-plan (ou simplement utilisez un terminal différent) :

$ ./plug &

Après cette action, vous devriez voir s'afficher :

The window ID is: 69206019

puis lancez le programme socket :

$ ./socket

Après avoir lancé socket, vous devriez voir les sorties suivantes sur le terminal :

I've been embedded.
A plug was added

La première ligne correspond à la sortie du programme plug, après qu'il a reçu notification qu'il a été incorporé dans l'objet Socket. La deuxième ligne a été émise par l'objet socket en réponse au signal plug_added. Si tout a été effectuée comme indiqué ci-dessus, la fenêtre socket doit ressembler grosso modo à quelque chose comme :
Si, pour une quelconque raison, l'objet Socket ne peut pas recevoir l'objet Plug, la fenêtre ressemblera à :