Développement de greffons pour LXPanel

De LXDE.org
Aller à : navigation, rechercher

Cette page ou ce contenu est sous licence GNU Free Documentation Licence (licence de documentation libre de GNU). Voir les détails de la licence.


Guide de développement de greffons pour LXPanel

Introduction

Ceci est un guide d'écriture de greffons (applets) pour LXPanel écrit par 2noob2banoob. Ce guide est sous licence GNU Free Documentation License (licence de documentation libre de GNU), c'est donc du contenu libre. Sentez-vous libre de le modifier et de le redistribuer. Ce guide a pour but d'aider les nouveaux à écrire leur premier greffon simple pour LXPanel. Il est cependant loin de la perfection, principalement car je suis moi-même nouveau dans le développement de greffons pour LXPanel. Je dirai donc parfois que je ne connais pas quelque chose ou je me tromperai. Si vous avez quelque suggestion que ce soit sur comment améliorer ce guide, vous pouvez m'envoyer un mél à 2noob2banoob@gmail.com ou à la liste de discussion de LXDE (lxde-list@lists.sourceforge.net).

Pré-requis

Autant que je sache, il n'existe pas d'autre pré-requis que d'avoir LXPanel d'installé et, bien sûr, des compétences basiques de programmation et de ligne de commande. Le seul fichier d'en-tête nécessaire est déjà disponible dans le paquet lxpanel. Bien sûr, il faut un compilateur C. Je vais faire comme si gcc est utilisé, qui est très probablement déjà installé sur votre système.

Choisir une méthode

Il existe plusieurs méthodes pour la compilation :

  • utiliser autotools : c'est la façon dont la plupart des composants de LXDE sont compilés. Bien que c'est la façon la plus habituelle, elle rend le processus de compilation inutilement long et génère des fichiers makefile difficilement lisibles par l'homme. Elle vous empêche ainsi de savoir exactement ce qui se produit lors du processus de compilation. Afin de modifier la manière dont le code est compilé, il faudra faire des essais et apprendre pour comprendre les fichiers d'entrée des outils autotools, ce qui peut être une vraie pénitence. C'est l'une des méthodes détaillées dans ce document ;
  • utiliser une des alternatives à autotools : je crois que PCMan essaye actuellement CMake, un système faisant à peu près la même chose qu'autotools. Je n'ai asolument aucune expérience de CMake ou de toute autre alternative à autotools et je ne le détaillerai pas dans ce didacticiel. Mais il est bon de savoir qu'elles existent ;
  • utiliser un fichier makefile écrit manuellement : bien que cette méthode n'est pas habituelle au sein du projet LXDE, je la préfère personnellement à l'utilisation des outils autotools. Cette méthode est simple et n'encombre pas votre répertoire de projet avec makefile qui génère des fichiers. De plus, éditer le fichier makefile est aisé car il ressemble plutôt à un script shell. C'est l'une des méthodes détaillées dans ce document ;
  • ne pas utiliser de fichier makefile : on peut également entrer manuellement toutes les commandes de compilation et d'installation manuellement dans son terminal. Je ne recommande pas cette méthode parce que la commande de compilation est très longue. Mais si vous êtes un vrai puriste, allez-y. Cette méthode n'est pas plus détaillée dans ce document. Mais les commandes de compilation sont principalement les mêmes que les commandes incluses dans les fichiers makefile écrits manuellement.

Construire le greffon en utilisant autotools

Pour commencer

Un bon point de départ serait le greffon example_plug fourni avec les sources de lxpanel-plugins.

Téléchargez-le avec la commande suivante :

$ git clone git://lxde.git.sourceforge.net/gitroot/lxde/lxpanel-plugins

Entrez dans le répertoire lxpanel-plugins. Vous pouvez alors supprimer tous les répertoires qui s'y trouvent sauf example_plug. Il peut s'avérer judicieux de réaliser une copie d'example_plug au cas où vous fassiez une bêtise. Bien que ce greffon est le plus petit par rapport au code source, il n'est pas complètement dépouillé. Pour commencer avec un greffon dépouillé, remplacez src/example.c par le example.c dépouillé que j'ai fait :

/**
* fichier : example.c
* description : Un greffon pour LXPanel complètement dépouillé ne faisant rien
* auteur: 2noob2banoob
* basé sur example_plug par lxde-develloppers
* GPL v2.0
*/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib/gi18n.h>

#include <string.h>
#include <lxpanel/plugin.h>

#define EVENT_MOUSE_BUTTON_PRESS_LEFT 1
#define EVENT_MOUSE_BUTTON_PRESS_MIDDLE 2
#define EVENT_MOUSE_BUTTON_PRESS_RIGHT 3

typedef struct {
Plugin * plugin;
GtkWidget *main;
GtkWidget *widget;
GtkTooltips *tip;
} Example;

gboolean button_press_event( GtkWidget *widget, GdkEventButton* event, Plugin* plugin)
{
if( event->button == EVENT_MOUSE_BUTTON_PRESS_LEFT )
{

}
else if( event->button == EVENT_MOUSE_BUTTON_PRESS_MIDDLE )
{

}
else if( event->button == EVENT_MOUSE_BUTTON_PRESS_RIGHT )
{
GtkMenu* popup = (GtkMenu*)lxpanel_get_panel_menu( plugin->panel, plugin, FALSE ); /* lxpanel_menu, can be reimplemented */
gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
return TRUE;
}

RET2(TRUE);
}

static gint update_tooltip(Example *egz)
{
char *tooltip;
ENTER;

tooltip = g_strdup_printf("LXPanel Example plugin");
gtk_tooltips_set_tip(egz->tip, egz->main, tooltip, NULL);
g_free(tooltip);

RET(TRUE);
}

static int example_constructor(Plugin *p, char** fp)
{
Example *egz;

/* initialization */
egz = g_new0(Example, 1);
egz->plugin = p;
p->priv = egz;

p->pwid = gtk_event_box_new(); //you may want to change this to a hbox or vbox if you want to use multiple widgets
GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
gtk_container_set_border_width( GTK_CONTAINER(p->pwid), 2 );

egz->widget = gtk_label_new("Hello World");
gtk_container_add(GTK_CONTAINER(p->pwid), egz->widget);

egz->main = p->pwid;
egz->tip = gtk_tooltips_new();
update_tooltip(egz);

g_signal_connect (G_OBJECT (p->pwid), "button_press_event", G_CALLBACK (button_press_event), (gpointer) p);
}

static void applyConfig(Plugin* p)
{
Example *egz = (Example *) p->priv;

update_tooltip(egz);
}

static void config(Plugin *p, GtkWindow* parent) {
GtkWidget *dialog;
Example *egz = (Example *) p->priv;
dialog = create_generic_config_dlg(_(p->class->name),
GTK_WIDGET(parent),
(GSourceFunc) applyConfig, (gpointer) p,
//_("displayed_varname"), &egz->variable, datatype, //datatype can be CONF_TYPE_STR, CONF_TYPE_INT, CONF_TYPE_BOOL etc.
NULL);
gtk_window_present(GTK_WINDOW(dialog));
}

static void example_destructor(Plugin *p)
{
ENTER;
Example *egz = (Example *)p->priv;
g_free(egz);
}

static void save_config( Plugin* p, FILE* fp )
{

}

PluginClass example_plugin_class = {

PLUGINCLASS_VERSIONING,

type : "example",
name : N_("Example plugin"),
version: "0.1",
description : N_("Example plugin: barebone doing nothing"),

constructor : example_constructor,
destructor  : example_destructor,
config  : config,
save  : save_config,
panel_configuration_changed : NULL
};

Avant de commencer à coder, il vaut mieux avoir un buildscript, à moins de vouloir tout faire de la manière par défaut. En règle générale, si on veut installer un préfixe non-standard ou avoir des dépendances installées dans un préfixe non-standard, il faudra passer des paramètres au script configure. Si on les met dans son buildscript, on ne risque pas de les oublier lorsqu'on exécute à nouveau le script configure. Si les sources, le repertoire contenant LXPanel et le répertoire de destination sont quelque part à l'intérieur de votre répertoire personnel, votre buildscript pourrait ressembler à ceci :

#!/bin/bash
# Modifiez ces variables
LXDEBUILD_SRC_ROOTDIR="$HOME/random_stuff/src"
LXDEBUILD_LXDE_SRC_ROOTDIR="$LXDEBUILD_SRC_ROOTDIR/lxde"
LXDEBUILD_PREFIX="$HOME/opt/lxde"
# Variables d'environnement
export PKG_CONFIG_PATH=$LXDEBUILD_PREFIX/lib/pkgconfig
export LD_LIBRARY_PATH=$LXDEBUILD_PREFIX/lib
export LD_RUN_PATH=$LXDEBUILD_PREFIX/lib
export CFLAGS="-I$LXDEBUILD_PREFIX/include"
export CPPFLAGS="-I$LXDEBUILD_PREFIX/include"
# Exécutez maintenant les commandes nécessaires à la compilationNow execute the commands needed to build
./autogen.sh && ./configure --enable-man --prefix=$LXDEBUILD_PREFIX --oldincludedir=$LXDEBUILD_PREFIX/include --with-libdir=$LXDEBUILD_PREFIX/lib --sysconfdir=$LXDEBUILD_PREFIX/etc && make && make install

Vous avez peut-être remarqué le commentaire « Modifiez ces variables ». Dans la plupart des cas, ces variables sont en fait les seules choses qui ont besoin d'être modifiées pour adapter le buildscript à sa situation.

Après avoir sauvegardé son buildscript, il faut s'assurer de le rendre exécutable avec

$ chmod +x

suivi du chemin vers son buildscript (ou simplement le nom si la fenêtre de terminal est dans le même répertoire que le script).

Votre première modification : un renommage

Il est maintenant temps de donner un nom à votre premier greffon. Il faudra penser à trois noms :

  • un nom complet qui peut contenir n'importe quel caractère mais il faut éviter les espaces et les doubles guillemets ;
  • un nom raccourci pouvant contenir uniquement des caractères habituels (A-Z, a-z, 0-9 et _) et pouvant être le même que le nom complet s'il ne contient pas de caractère interdit ;
  • le nom de la classe qui sera utilisé dans le code source. Les conventions de codage suggèrent qu'il commence par une lettre majuscule.

Le script suivant appliquera automatiquement ces noms dans tous les fichiers où c'est nécessaire (encore une fois, n'oubliez pas de le rendre exécutable) :

#!/bin/sh

[ $# -lt 3 ] && echo "Veuillez passer les paramètres comme suit :
$0 nomcourt nomlong nomdeclasse." && exit 1

sed -i "s/example-plug/$1/g" configure.ac
sed -i "s/example/$1/g" Makefile.am
cd po
sed -i "s/example/$1/g" POTFILES.in
cd ../src
sed -i "s/example/$1/g" Makefile.am
sed -i "s/example/$1/g" example.c
sed -i "s/Example plugin/$2/g" example.c
sed -i "s/Example/$3/g" example.c
mv example.c $1.c

echo "Les noms ont été changés."

Compiler le greffon avec un makefile écrit manuellement

Il faut d'abord un fichier source contenant le greffon. Je fais comme s'il s'appelle lxpanelexample.c. Ce qui suit pourrait être le contenu d'un fichier source de greffon dépouillé :

/**
* Fichier : example.c
* Description : Un greffon pour LXPanel complètement dépouillé ne faisant rien
* Auteur : 2noob2banoob
* Basé sur example_plug par lxde-develloppers
* GPL v2.0
*/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib/gi18n.h>

#include <string.h>
#include <lxpanel/plugin.h>

#define EVENT_MOUSE_BUTTON_PRESS_LEFT 1
#define EVENT_MOUSE_BUTTON_PRESS_MIDDLE 2
#define EVENT_MOUSE_BUTTON_PRESS_RIGHT 3

typedef struct {
Plugin * plugin;
GtkWidget *main;
GtkWidget *widget;
GtkTooltips *tip;
} Example;

gboolean button_press_event( GtkWidget *widget, GdkEventButton* event, Plugin* plugin)
{
if( event->button == EVENT_MOUSE_BUTTON_PRESS_LEFT )
{

}
else if( event->button == EVENT_MOUSE_BUTTON_PRESS_MIDDLE )
{

}
else if( event->button == EVENT_MOUSE_BUTTON_PRESS_RIGHT )
{
GtkMenu* popup = (GtkMenu*)lxpanel_get_panel_menu( plugin->panel, plugin, FALSE ); /* lxpanel_menu, can be reimplemented */
gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
return TRUE;
}

RET2(TRUE);
}

static gint update_tooltip(Example *egz)
{
char *tooltip;
ENTER;

tooltip = g_strdup_printf("LXPanel Example plugin");
gtk_tooltips_set_tip(egz->tip, egz->main, tooltip, NULL);
g_free(tooltip);

RET(TRUE);
}

static int example_constructor(Plugin *p, char** fp)
{
Example *egz;

/* initialization */
egz = g_new0(Example, 1);
egz->plugin = p;
p->priv = egz;

p->pwid = gtk_event_box_new(); //you may want to change this to a hbox or vbox if you want to use multiple widgets
GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
gtk_container_set_border_width( GTK_CONTAINER(p->pwid), 2 );

egz->widget = gtk_label_new("Hello World");
gtk_container_add(GTK_CONTAINER(p->pwid), egz->widget);

egz->main = p->pwid;
egz->tip = gtk_tooltips_new();
update_tooltip(egz);

g_signal_connect (G_OBJECT (p->pwid), "button_press_event", G_CALLBACK (button_press_event), (gpointer) p);
}

static void applyConfig(Plugin* p)
{
Example *egz = (Example *) p->priv;

update_tooltip(egz);
}

static void config(Plugin *p, GtkWindow* parent) {
GtkWidget *dialog;
Example *egz = (Example *) p->priv;
dialog = create_generic_config_dlg(_(p->class->name),
GTK_WIDGET(parent),
(GSourceFunc) applyConfig, (gpointer) p,
//_("displayed_varname"), &egz->variable, datatype, //datatype can be CONF_TYPE_STR, CONF_TYPE_INT, CONF_TYPE_BOOL etc.
NULL);
gtk_window_present(GTK_WINDOW(dialog));
}

static void example_destructor(Plugin *p)
{
ENTER;
Example *egz = (Example *)p->priv;
g_free(egz);
}

static void save_config( Plugin* p, FILE* fp )
{

}

PluginClass lxpanelexample_plugin_class = {

PLUGINCLASS_VERSIONING,

type : "lxpanelexample",
name : N_("Barebone LXPanel plugin"),
version: "0.1",
description : N_("Example plugin: barebone doing nothing"),

constructor : example_constructor,
destructor  : example_destructor,
config  : config,
save  : save_config,
panel_configuration_changed : NULL
};

Il faut ensuite un makefile. Le makefile suivant est probablement un bon premier jet :

# Modifiez ces variables.
LXDEBUILD_PREFIX=${HOME}/opt/lxde
LXDEBUILD_PLUGIN_NAME=lxpanelexample
LXDEBUILD_SOURCEFILES=example.c
LXDEBUILD_VERSION_SCRIPT=example.ver

# Ces variables ne devraient probablement pas être modifiées. Elles sont uniquement pour commodité.
LXDEBUILD_DATA_DIR=${LXDEBUILD_PREFIX}/share
LXDEBUILD_LIB_DIR=${LXDEBUILD_PREFIX}/lib
LXDEBUILD_LXPANEL_PLUGIN_DIR=${LXDEBUILD_LIB_DIR}/lxpanel/plugins
LXDEBUILD_DEPENDENCIES=lxpanel

# Variables d'environnement. Elles ne devraient pas être modifiées non plus.
export PKG_CONFIG_PATH=${LXDEBUILD_LIB_DIR}/pkgconfig
export LD_LIBRARY_PATH=${LXDEBUILD_LIB_DIR}
export LD_RUN_PATH=${LXDEBUILD_LIB_DIR}

# Cible Phony invoquée lorsque make est exécuté sans argument.
# Dans ce cas, il invoque la cible de lxpanelplugin sans rien faire d'autre.
all: lxpanelplugin

# Cible pour la compilation de votre greffon pour LXPanel.
lxpanelplugin:
gcc -shared -fPIC -g -O2 \
-DPIC -DHAVE_CONFIG_H -DPACKAGE_DATA_DIR=\""${LXDEBUILD_DATA_DIR}"\" \
`pkg-config --libs --cflags ${LXDEBUILD_DEPENDENCIES}` \
-o ${LXDEBUILD_PLUGIN_NAME}.so \
-Wl,-soname -Wl,${LXDEBUILD_PLUGIN_NAME}.so -Wl,-version-script -Wl,${LXDEBUILD_VERSION_SCRIPT} \
${LXDEBUILD_SOURCEFILES}

# Cible pour l'installation du greffon compilé.
install: lxpanelplugin
cp ${LXDEBUILD_PLUGIN_NAME}.so ${LXDEBUILD_LXPANEL_PLUGIN_DIR}

Cela peut sembler être une surcharge de variables. Mais, en comparaison des makefiles générés, c'est un soulagement. À long terme, lancer ces variables pourrait augmenter la maintenabilité de votre code. Ça pourrait aussi rendre le renommage de votre greffon ainsi que son installation vers un préfixe différent ou l'ajout d'une autre dépendance plus facile.

Vous avez peut-être remarqué la mention d'un fichier appelé « version script ». Comme vous avez pu le deviner, ce fichier doit se trouver dans votre répertoire personnel également. Comme le greffon se compile dans une bibliothèque, certaines parties devront être accessibles à une application le chargeant (typiquement, LXPanel). Le script version s'occupe d'indiquer au compilateur quelles parties devraient être accessibles et lesquelles sont uniquement pour l'usage interne de la bibliothèque. Le script version ressemble typiquement à ceci :

{ global:
button_press_event;
lxpanelexample_plugin_class;
scroll_event;
local: *; };

Bien sûr, lxpanelexample_plugin_class devrait être renommé si on renomme le greffon.

Maintenant que le fichier source, le makefile et la script version sont en place, il est temps de compiler :

$ make && make install

Si tout s'est bien passé, vous devriez maintenant pouvoir ajouter voutre greffon à LXPanel. Il devrait consister en une étiquette disant « Hello World ».

Structure du fichier source

Tout fichier source de greffon de LXPanel contient au moins les éléments suivants :

  1. Les inclusions ;
  2. Un struct avec un typedef définissant les données dont votre greffon doit garder la trace afin de fonctionner correctement. Le struct contient les variables nécessaires à tout greffon pour LXPanel ainsi que les variables spécifiques à votre greffon. Les variables nécessaires à tout greffon sont :
    • le greffon *plugin : un pointeur vers un struct rendant effectivement votre greffon un greffon pour LXPanel,
    • GtkWidget *main : le principal widget GTK, c'est un conteneur contenant les autres widgets,
    • GtkTooltips *tip : l'infobulle que les utilisateurs obtiennent lorqu'ils passent sur votre greffon ;
  3. GtkWiget *widget n'est pas nécessaire, bien qu'il est recommandé d'inclure votre (vos) widget(s) fonctionnel(s) comme variable(s) dans votre struct. On peut nommer cette variable come on veut. Si on veut utiliser plusieurs widgets, on peut les inclure dans des variables séparées ou dans un tableau, suivant sa préférence personnelle ;
  4. La fonction gboolean button_press_event( GtkWidget *widget, GdkEventButton* event, Plugin* plugin). Cette fonction est appelée lorsqu'un greffon est cliqué avec n'importe quel bouton de souris ;
  5. Le constructeur : static int example_constructor(Plugin *p, char** fp) ;
  6. Le destructeur : static void example_destructor(Plugin *p) ;
  7. La fonction static void applyConfig(Plugin* p) appelée si une nouvelle configuration doit être appliquée. C'est habituellement le cas lors d'un clic sur le bouton « Appliquer » ou « OK » de la fenêtre de préférences du greffon ;
  8. La fonction static void config(Plugin *p, GtkWindow* parent). Cette fonction affiche la fenêtre des préférences ;
  9. La fonction static void save_config( Plugin* p, FILE* fp ) appelée si une configuration doit être sauvegardée dans une fichier. C'est habituellement le cas lors d'un clic sur le bouton « Appliquer » ou « OK » de la fenêtre de préférences du greffon ;
  10. PluginClass example_plugin_class qui est une instanciation du struct PluginClass et nécessaire pour que LXPanel reconnaisse effectivement votre greffon.

Que modifier ?

La première chose que vous voulez réimplémenter est le sonctructeur. Vous voulez remplacer l'étiquette « Hello World » par un widget faisant ce que vous voulez que votre greffon fasse. Ou, bien sûr, avec de multiples widgets, auquel cas vous devrez transformer votre widget principal en un conteneur de multiples widgets (typiquement un hbox ou un vbox). Si vous ajoutez des événements, il faudra créer des fonctions de rappel et les connecter aux signaux indiquant vos événements. Une fois que vous pensez que votre greffon possède ses fonctionnalités basiques, il est temps de le compiler et de l'essayer.

Pour ce faire, ouvrez le répertoire contenant votre greffon dans un terminal, lancez votre buildscript dans le cas où vous utilisez autotools ou tapez

$ make && make install

dans le cas où vous utilisez un makefile écrit manuellement. Enfin, si ça réussit, ajouter le greffon à votre LXPanel. Une fois que ça fonctionne, vous voudrez peut-être ajouter la possibilité de configurer le greffon. Ceci se fait en modifiant les fonctions possédant « config » dans leur nom. Vous devriez maintenant trouver ceci tout(e) seul(e).