Comment écrire des greffons pour LXPanel

De LXDE.org
Aller à : navigation, rechercher

Cet article explique comment écrire des greffons pour LXPanel.

Conditions préalables

Voici les suppositions faites sur les paquets lors du développement de ce tutoriel :

  • LXPanel est en version 0.7.0 ou supérieure ;
  • LibFM est en version 1.2.0 ou supérieure ;
  • GTK+ est en version 2.18.0 ou supérieure.
Note :
En raison d'une erreur dans le fichier lxpanel.c, libmenu-cache était également nécessaire ; ceci a été résolu dans la version 0.7.1.


Les bibliothèques nécessaires pour compiler un greffon pour LXPanel avec succès sont (installées depuis la source ou en utilisant un gestionnaire de paquets comme apt-get et yum) :

  • lxpanel-devel ;
  • libfm-devel ;
  • gtk2-devel.

Il est également admis une connaissance de niveau intermédiaire du langage C (structure, pointeurs et pointeurs de fonctions) et un même niveau de connaissance de GTK+ (en fonction de la complexité du greffon).

Directives

Un greffon pour LXPanel peut être vu comme possédant trois niveaux : le niveau d'interface de programmation d'application (API) ou de classe, le niveau GTK et le niveau privé.

Niveau API

Au plus haut niveau, il existe deux structures C à connaître : PluginClass et Plugin.

La structure PluginClass contient un jeu de fonctions permettant au greffon d'être manipulé. Pendant sa durée de vie, LXPanel crée une instance de classe (et une seule) pour chacun des greffons disponibles — ceci se produit soit à l'ouverture de LXPanel soit quand « Ajouter/Enlever des élements au tableau de bord » est sélectionné suite à un clic droit sur le panneau. Une fois qu'une instance de classe de greffon est créée, toutes les interactions avec elle sont faites à travers les fonctions disponibles : constructor, destructor, config, save et panel_configuration_changed.

La structure PluginClass est définie comme suit (directement depuis le fichier /usr/include/lxpanel/plugin.h) :

typedef struct {
   unsigned short structure_size;     /* Taille de structure, pour le support des versions */	
   unsigned short structure_version;  /* Version de structure, pour le support des versions */  
   char * fname;		       /* Nom du chemin vers le fichier du greffon */
   int count;			       /* Compteur de référence */
   GModule * gmodule;	               /* Structure GModule associée */
   int dynamic : 1;		       /* Vrai si chargé dynamiquement */
   int unused_invisible : 1;          /* Inutilisé ; bit réservé */
   int not_unloadable : 1;	       /* Non inchargeable à cause d'une restriction de GModule */
   int one_per_system : 1;	       /* Spécial : un seul possible par système, comme la notification système */
   int one_per_system_instantiated : 1;/* Vrai si une instance existe */
   int expand_available : 1;	       /* Vrai si l'option "stretch" est disponible */
   int expand_default : 1;	       /* Vrai sir l'option "stretch" est par défaut */
   /* Ces champs pointent à l'intérieur de l'image du greffon (considéré comme le niveau privé) */
   char * type;	        /* Nom interne du greffon à faire correspondre avec le nom de fichier externe */
   char * name;		/* Nom d'affichage du greffon pour sélection graphique */
   char * version;		/* Version du greffon */
   char * description;	        /* Description textuelle brève du greffon pour sélection graphique */
   int (*constructor)(struct _Plugin * plugin, char ** fp);     /* Crée une instance du greffon */
   void (*destructor)(struct _Plugin * plugin);                 /* Détruit une instance du greffon */
   void (*config)(struct _Plugin * plugin, GtkWindow * parent); /* Demande au graffon d'afficher son interface de configuration */ 
   void (*save)(struct _Plugin * plugin, FILE * fp); 	         /* Demande au greffon de sauvegarder sa configuration dans un fichier */
   void (*panel_configuration_changed)(struct _Plugin * plugin);/* Demande au greffon de se redessiner après un changement de configuration du panneau */
 } PluginClass;

La structure Plugin est un conteneur utilisé par LXPanel pour gérer un greffon et ses trois niveaux. On se réfère au niveau d'interface de programmation d'application par la variable « class ». On se réfère au niveau GTK par la variable « pwid ». Enfin, on se réfère au niveau privé par la variable « priv ». Cette structure contient aussi des options spécifiques à GTKWidget utilisées par LXPanel (« expand », « padding » et « border »).

La structure Plugin est définie comme suit :

typedef struct _Plugin {
   PluginClass * class;	/* Pointeur de retour vers PluginClass */
   Panel * panel;		/* Pointeur de retour vers le panneau */
   GtkWidget * pwid;		/* Widget de niveau supérieur ; le greffon alloue,
mais le mécanisme de greffons (pas le greffon lui-même) le détruit */
   int expand;			/* Réglage de l'extension pour le conteneur */
   int padding;		        /* Réglage du remplissage pour le conteneur */
   int border;			/* Réglage des bordures pour le conteneur */
   gpointer priv;		/* Contexte privé pour le greffon ; le greffon le libère dans son destructor */
} Plugin;

Niveau GTK

Une fois que la classe du greffon est instanciée, elle est disponible pour être ajoutée au panneau. Si l'utilisateur choisit d'ajouter un greffon, le panneau demande au greffon de créer sa représentation graphique (penser au graphique mobile du greffon d'activité du processeur central ou aux deux écrans d'ordinateur du greffon de réseau). C'est au greffon de créer tout ce dont il a besoin, y compris, sans s'y limiter, la gestion des clics de boutons de souris, les notifications système, les icpones et toute autre chose pouvant s'avérer nécessaire. Bien que c'est au greffon de créer le widget GTK (référencé par « pwid » à l'intérieur de la structure Plugin), LXPanel le détruit en fait une fois que l'utilisateur retire le greffon du panneau.

Niveau privé

Au niveau privé, un greffon est autorisé à avoir une représentation interne de son état. En d'autres termes, ni LXPanel, ni la structure PluginCLass ne fournissent au développeur du greffon un espace réservé pour la configuration spécifique au greffon ou toute autre information d'état spécifique au greffon. C'est ici qu'un développeur définit sa propre structure et l'assigne, sur une base par instance, à la variable « priv » du greffon. L'exemple dans la partie guide devrait clarifier tout point confus dans cette explication.

La durée de vie d'un greffon est aussi quelque chose à garder en tête en développant un greffon. En général, la séquence suivante permet l'interaction entre LXPanel et un greffon (traduction de cette phrase à vérifier) :

  1. La structure PluginClass spécifique au greffon est instanciée.
  2. Le greffon est ajouté au panneau et le « constructor » du greffon est appelé.
  3. Les modifications au panneau sont effectuées en fonction des interactions normales de l'utilisateur. À ce moment, les fonctions « save », « config » et « panel_configuration_changed » du greffon peuvent être appelées dans n'import quel ordre et devrait être gérées de façon appropriées.
  4. Le greffon est retiré du panneau (la fonction « destructor » est appelée).

Le niveau API d'un greffon, un greffon est « vivant » tant que l'instance du panneau est active (et a accès au module du greffon [fichier .so] — sauf si c'est un greffon lié statiquement). Le niveau GTK d'un greffon est « vivant » depuis l'exécution de la fonction « constructor » (c'est au développeur de créer le widget GTK approprié pour le greffon) jusqu'à ce que le greffon soit retiré du panneau (le panneau détruit en fait le widget GTK s'il existe toujours après l'appel à la fonction « destructor »). Le niveau privé d'un greffon est « vivant »depuis l'exécution de la fonction « constructor » (c'est la responsabilité du développeur d'allouer la structure appropriée) jusqu'à l'exécution de la fonction « destructor » (c'est la responsabilité du développeur de faire le nettoyage).

Guide

Après avoir vu les informations générales à propos de LXPanel et des greffons, voici comment créer un greffon basique.

Comme expliqué dans le guide de 2noob2banoob (voir le lien externe), il existe plusieurs façons d'appréhender la compilation d'un greffon. Dans l'exemple ci-dessous, nous ferons tout manuellement. Le développeur devra ainsi générer les scripts makefile ou autoconf appropriés une fois qu'il se sentira à l'aise avec l'infrastucture du greffon.

La feuille de route de ce guide est la suivante :

  1. Création du fichier source du greffon.
  2. Compilation du fichier source en une bibliothèque partagée.
  3. Essais du greffon à l'intérieur de l'instance LXPanel en cours d'exécution.

D'abord, il faut inclure tout ce qui sera nécessaire dans notre exemple — particulièrement le fichier lxpanel/plugin.h.

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib/gi18n.h>

#include <lxpanel/plugin.h>

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

Ensuite, il faut tirer profit de la variable « priv » du greffon pour qu'il garde une trace de combien d'instances sont actuellement en cours.

// Interne à la source du greffon, pas utilisé par la variable « priv »
static int iInstanceCount = 0;

/* L'identifiant du greffon -- une instance de cette structure est ce qui va être assigné à « priv » */
typedef struct 
{
  gint iMyId;
} TestPlugin; 

Il faut ensuite définir nos versions du « constructor » de PluginClass et les autres fonctions

constructor :

static int 
test_constructor(Plugin * p, char ** fp)
{
 /* fp est un pointeur vers le flux du fichier de configuration.
      Comme on ne l'utilise pas, on s'assure qu'il ne produit
      pas d'erreur à la compilation. */
 (void)fp;

 // Allocation de notre instance de structure privée
 TestPlugin *pTest = g_new0(TestPlugin, 1);

 // Le mettre où il doit être
 p->priv = pTest;

 // Mise à jour du compteur d'instances
 pTest->iMyId = ++iInstanceCount;

 // Création d'une étiquette à partir de l'identifiant
 char cIdBuf[10] = {'\0'};

 sprintf(cIdBuf, "TP-%d", pTest->iMyId);

 GtkWidget *pLabel = gtk_label_new(cIdBuf);

 // Besoin de créer un widget pour l'afficher
 p->pwid = gtk_event_box_new();

 // Réglage de la largeur de la bordure
 gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 1);

 // Ajout de l'étiquette au conteneur
 gtk_container_add(GTK_CONTAINER(p->pwid), GTK_WIDGET(pLabel));

 // Réglage de la taille voulue
 gtk_widget_set_size_request(p->pwid, 40, 25);

 // Le widget n'a pas de fenêtre…
 gtk_widget_set_has_window(p->pwid, FALSE);

 // Affichage du widget
 gtk_widget_show_all(p->pwid);

 // Succès !!!
 return 1;
}

destructor :

static void 
test_destructor(Plugin * p)
{
  // Décrémentation du compteur d'instances locales
  --iInstanceCount;

  // Recherche de l'instance de structure privée
  TestPlugin *pTest = (TestPlugin *)p->priv;

  // La libérer -- pas besoin de s'inquiéter de libérer « pwid », le panneau s'en charge
  g_free(pTest);
}

configure :

static void test_configure(Plugin * p, GtkWindow * parent)
{
  // Ne fait rien ici. Il faut donc s'assurer qu'aucun paramètre
  // n'émet d'avertissement lors de la compilation.
  (void)p;
  (void)parent;
}

save_configuration:

static void test_save_configuration(Plugin * p, FILE * fp)
{
  // Ne fait rien ici. Il faut donc s'assurer qu'aucun paramètre
  // n'émet d'avertissement lors de la compilation.
  (void)p;
  (void)fp;
}

Pour terminer, il faut définir le PluginClass du greffon :

/* Description du greffon */
PluginClass test_plugin_class = {

   // C'est un #define se préoccupant des variables de taille et de version
   PLUGINCLASS_VERSIONING,

   // Type de ce greffon
   type : "test",
   name : N_("TestPlugin"),
   version: "1.0",
   description : N_("Lance un greffon d'essai."),

   // On peut avoir plusieurs instances lancées en même temps
   one_per_system : FALSE,

   // On ne peut pas étendre ce greffon
   expand_available : FALSE,

   // Assignation des fonctions aux pointeurs fournis
   constructor : test_constructor,
   destructor  : test_destructor,
   config : test_configure,
   save : test_save_configuration
};

C'est tout.

Il faut remarquer qu'aucune implémentation de la fonction « panel_configuration_changed » n'a été fournie. LXPanel est suffisamment gentil pour ne pas appeler de fonction non définie dans un greffon. Ceci étant, il vaut quand même mieux fournir au moins les fonctions « constructor » et « destructor » du greffon de façon à ce que le greffon sache comment créer — et nettoyer après lui.

Une autre précision importante : LXPanel s'attend à ce qui suit :

  • la variable « type » à l'intérieur de la définition du greffon doit avoir le même nom que celui de la bibliothèque partagée dans laquelle on la trouve (en d'autres termes, même si le fichier source est nommé testplugin.c, comme on a défini le type comme « test », il faut créer une bibliothèque partagée nommée test.so) ;
  • le nom de la classe du greffon doit correspondre à la variable type suivie de la chaîne de caractères « _plugin_class » (en d'autres termes, si on définit le type du greffon comme « test », alors, la structure qu'on définit doit s'appeler « test_plugin_class »).

Maintenant, il faut essayer de créer une bibliothèque partagée pour le greffon (source sauvegardée comme testplugin.c) :

# gcc -Wall `pkg-config --cflags lxpanel gtk+-2.0` -shared -fPIC testplugin.c -o test.so `pkg-config --libs lxpanel gtk+-2.0`

La ligne ci-dessus utilise gcc pout compiler testplugin.c dans un fichier objet partagé nommé test.so en utilisant pkg-config pour fournir les drapeaux au compilateur C et , à la fin, les drapeaux de bibliothèque pour lxpanel et gtk+-2.0 (comme ces elles sont toutes les deux des dépendances du greffon).

Après avoir placé le fichier test.so résultant dans /usr/lib/lxpanel/plugins (c'est l'endroit où LXPanel recherche de nouveaux greffons dynamiques), on peut effectuer un clic-droit sur le panneau et essayer d'ajouter un greffon eu panneau :

Screenshot-AddTestPlugin.png

Après avoir ajouté des greffons d'essai, le panneau ressemble à ceci :

Screenshot-TestPluginInAction.png

Te les préférences ressemblent à ceci :

Screenshot-PanelPreferences.png

Maintenant, allez de l'avant et créez des greffons !

Voir aussi

LXPanel

Lien externe

Guide pour LXPanel (en anglais)