Next: Recherche d'aide!, Previous: Programmation d'adesklets, Up: Top
adeskets a été fait pour être facilement utilisable depuis toutes sortes d'environnements de langages1. Ce chapitre explique aux développeurs comment l'interpréteur d'adesklets s'intègre dans le système d'une manière indépendante du langage.
Si vous n'êtes pas un minimum habitués aux systèmes POSIX ou que vous n'avez pas les notions de base sur la programmation de systèmes d'exploitations, ce chapitre n'est surement pas d'un grand interêt pour vous.
Veillez noter que le paquetage Python du répertoire scripting/pyhton/adesklets est un bon complément de ce que vous allez lire.
Si vous voulez utiliser l'interpréteur d'adesklets depuis votre langage favori mais qu'il n'est pas supporté à la base2, vous pouvez toujours ajouter ce support, pourvu que votre langage comporte une de ces fonctionalités:
Il doit aussi pouvoir3:
Enfin, il doit aussi être capable de lancer un processus fils.
Ces pré-requis sont bien minces du point de vue d'un système POSIX, mais précisons par souci d'exhaustivité que si votre environnement de langage ne remplit pas ces critères, vous pouvez quand même vous en servir pourvu que vous arriviez à faire en sorte que votre application communique de manière fiable avec un autre programme 4 qui, lui, les remplirait. Bien sur, ce n'est pas la situation idéale et cela doit être évité autant que possible.
Comme vous le savez probablement (See Utiliser adesklets.), adesklets possède deux modes de fonctionnement, si l'on peut les appeler ainsi:
Dans une nouvelle session X, la séquence de démarrage classique est à peu près la suivante:
ADESKLETS_ID
contenant leur propre
ID entier en tant que chaine de caractères, puis
exécutent5 le script associé.ADESKLETS_ID
sans
l'alterer. Il est du devoir du desklet de s'assurer que:
stdin
, stdout
et
stderr
, soient redirigés
séparément, de sorte que le processus parent
puisse facilement écrire dans le stdin
d'adesklets, et lire stdout
et
stderr
. L'utilisation de tuyaux, FIFOs ou
éventuellement de fichiers réguliers sont
possibles.stdin
et les
lectures depuis les deux autres flux se font toutes sans
tampon.ADESKLETS_ID
et le nom absolu
donné en premier argument de ligne de commande pour
déterminer ses caracteristiques non-scriptables6 à partir de $HOME/.adesklets. Une fois cette
opération terminée, l'évènement
ready!
est géneré (See Evènements.),
l'interpréteur est alors prêt à traiter des
commandes.Quand un nouveau desklet est appelé directement, la
séquence de démarrage décrite dans la
section précedente est la même, sauf que les trois
premières étapes n'ont tout simplement pas lieu.
Cela signifie que l'environnement ADESKLETS_ID
n'est
pas défini; cela permet au nouvel interpréteur
d'adesklets de
détecter qu'il s'agit d'un nouveau desklet, il va
parcourir le fichier de configuration $HOME/.adesklets et allouer au desklet le
premier ID disponible.
Il faut noter que si le nom de fichier du
desklet donné comme premier argument à l'instance
fille d'adesklets est
relatif ou si'l n'est pas lisible et exécutable par
l'utilisateur courant, le desklet ne sera pas enregistré
dans $HOME/.adesklets, et
par conséquent ne sera pas automatiquement
redémarré par le lanceur dans les futures nouvelles
sessions X. Dans le cas où stdin
est un
terminal7, l'enregistrement ne se fera pas non
plus.
Ainsi, du point de vue du script de desklet, les deux cas sont identiques, ce qui ne nécessite aucun traitement conditionel. Tout le travail d'initialisation du desklet se fait dans la quatrième étape de la sous-partie ci-dessus.
Si besoin, le desklet pourra utiliser la
commande get_id
pour déterminer son ID.
Comme expliqué précedemment, un desklet communique avec son propre interpréteur adesklets via des flux sans tampons redirigés. Voyons ici quelles informations ils transportent.
Sur le stdin
de l'interpréteur, le desklet
envoie les commande qu'il veut exécuter dans l'ordre dans
lequel il veut les exécuter, une commande par ligne8.
Etant donné que la lecture de ce flux est suspendue quand
une commande est évaluée, un desklet devra
géneralement attendre qu'une commande soit
exécutée avant d'en envoyer une autre 9
(voir la section suivante).
Le format des commandes est assez simple: on
a le nom d'une commande suivi d'arguments séparés
par des espaces, tous représentés par du texte pur,
y compris les nombres. adesklets ne se soucie pas du nombre
d'espaces entre les arguments, pourvu qu'il y en ait au moins un.
Dans le cas particulier des commandes où le dernier
argument est une chaine de caractères
(text_draw
, par exemple), adesklets
considèrera les premiers caractères n'étant
pas des espaces comme le début de la chaine; le reste de
la ligne, espaces inclus, sera considéré comme
faisant partie de la chaine.
Deux fichiers, automatiquement tenus à
jour avec la source (scripting/prototypes et scripting/enums) à partir de la
distribution d'origine, fournissent au format texte brut (avec
une tabulation comme séparateur) une description de toutes
les commandes et constantes mises à disposition des
desklets10. Un bon portage pour un langage
particulier devrait être fourni avec quelques routines
permettant de génerer automatiquement les parties
pertinentes de sa propre bibliothèque à partir de
ces fichiers; par exemple, le portage de Python comporte une
fonction protoize
dans le script setup.py de distutils
pour le
faire. Idéalement, cette routine devrait être
écrite avec ce langage et un ensemble raisonnable de
librairies11; si'l n'est pas bien adapté
à cette tâche, vous devrez vous limitez
à:
si vous voulez que votre travail soit intégré au paquetage officiel.
L'interpréteur d'adesklets cherche fréquemment si
de nouveaux caractères sont arrivés sur son flux
stdin
, et le lit si besoin. Dès qu'il
reçoit le caractère de fin de ligne ('\n'), il
arrête de lire et essaye d'exécuter la commande. Une
fois la commande terminée (elle aura peut-être
envoyé quelques lignes sur stdout
), il envoie
en tant que dernière entrée un message
d'état de la forme suivante:
command RANG ok: MESSAGE
si la commande s'est terminée avec succès, ou:
command RANG error: MESSAGE_DERREUR
RANG
est l'indentifiant
numérique de la commande (un entier non signé) qui
part de zéro lorsque l'interpréteur est
créé, et qui est automatiquement
incrémenté à chaque ligne qu'il
reçoit ensuite. Il est garanti que toutes les commandes
seront exécutées dans l'ordre dans lequel elles ont
étées données. MESSAGE_DERREUR
est un message compréhensible, sans format particulier,
expliquant d'où vient l'erreur. MESSAGE
est
également compréhensible, mais sera formaté
de sorte qu'il peut être utilisé pour
récupérer les valeurs de retour utiles
algorithmiquement de n'importe quelles commandes. Ces valeurs de
retour peuvent être:
create_image
et autres)context_get_color
et consorts)stdout
(images_info
et ses semblables), la
dernière ligne étant un message
d'étatAlgorithmiquement, votre 'routine de
récupération des valeurs de retour' devrait d'abord
essayer de récupérer les entiers de
MESSAGE
13. Si elle trouve un entier ou plus,
elle devra vérifier que le message n'est pas une simple
copie d'une commande donnée; alors la valeur de retour
correspond au cinquième cas (cf ci-dessus). Autrement, si
elle ne trouve qu'un entier, on est dans le premier cas; et si'l
y en a plus d'un, dans le deuxième. Si aucun entier n'est
trouvé et si'l y avait une liste de lignes envoyées
à stdout
avant le message d'état de la
commande, on est dans le troisième cas. Pour le reste: si
le message d'état diffère d'une commande
donnée, cela correspond au quatrième cas. Sinon il
faut considérer que la sortie correspond au
cinquième cas. Cet algorithme de
récupération fonctionne avec toutes les commandes
listées dans scripting/prototypes.
Tous les autres rapports asynchrones
d'évènements sont envoyés au flux
stderr
de l'interpréteur. Ils sont
asynchrones par rapport au traitement des commandes; ils ne se
produiront jamais pendant le traitement d'une commande (dans
l'intervalle de temps entre la soumission de la commande
complète sur stdin
et la sortie de son
message d'état sur stdout
), ils peuvent
être envoyés à n'importe quel autre moment
14.
Les rapports d'évènements tiennent sur une ligne, de la forme 15:
event: MESSAGE_DEVENEMENT
Sous la forme de Backus Naur16, la grammaire employée pour MESSAGE_DEVENEMENT serait:
MESSAGE_DEVENEMENT :: ready! | backgroundgrab | menufire MENU_ID MENU_STR | motionnotify X Y | enternotify X Y | leavenotify X Y | buttonpress X Y BUTTON_ID | buttonrelease X Y BUTTON_ID ;;
où MENU_ID
,
X
, Y
, et BUTTON_ID
sont
tous des entiers supérieurs ou égaux à
zéro, et MENU_STR
est une chaine de
caractères, pouvant contenir des espaces.
Détaillons un peu plus tout cela:
Tous les évènements sauf
'Ready!' (nominativement: MotionNotify, EnterNotify, LeaveNotify,
ButtonPress, ButtonRelease, BackgroundGrab et MenuFile) peuvent
êtres masqués par le desklet. En fait,
adesklets fournit au
desklet des commandes permettant de gérer
spécifiquement la création et la production des
évènements, qui sont: event_catch
,
events_get_send_sigusr1
,
events_reset_all
, event_uncatch
,
events_info
, events_set_echo
,
events_get_echo
, events_purge
et
events_set_send_sigusr1
. Il faut noter que ces
fonctions ne sont pas listées dans scripting/prototypes puisque vous ne voulez
probablement pas que vos utilisateurs y aient accès. Vous
devrez probablement démarrer une session interactive et
jouer avec pour être sur que vous les avez bien comprises.
Il est aussi temps de mentionner le script src/adesklets_debug.sh, qui se rend utile
pour les expériences intéractives avec les
évènements.
Il faut encore mentionner deux choses à propos des évènements:
event_purge
. Vous
pouvez modifier cela avec events_set_echo
.Events_handler
.Pourvu que votre langage le permette, vous devriez installer une sorte de récuperateur de SIGCHILD, ainsi vous pourrez au moins être averti si jamais l'interpréteur d'adesklets se termine sans raison (sans parler du cas des processus fils à l'état zombie)17.
adesklets supporte aussi l'exécution differée (mode indirect) et le remplacement textuel des variables. Voici comment fonctionnent les variables en mode indirect:
start_recording
et
stop_recording
)play
, See
Programmation d'adesklets.), les variables sont
substituées en une seule passe, et aucune
réference indirecte n'est possible. La substitution est
assez directe. On recherche dans la ligne de commande
stockée toute séquence non-vide de
caractères distincts de l'espace commançant par
un seul '$'. Chaque séquence trouvée est
remplacée par la valeur $variable
correspondante, ou supprimée si aucune variable n'est
trouvée.Le même mécanisme de substitution s'applique en mode d'exécution direct. Tout ceci implique que, avec le paquetage Python par exemple, ces deux morceaux de code sont équivalents:
adesklets.window_resize(100,100)
et:
adesklets.set('dim',100) adesklets.window_resize('$dim','$dim')
Python étant un langage typé dynamiquement, aucune modification de notre code n'est nécessaire, mais d'autres langages pourraient demander plus de réflexion.
Maintenant vous devriez savoir quasiment tout ce qu'il faut pour intégrer adesklets à votre environnement de langage. Cette tache peut avoir l'air intimidante, mais un traducteur très propre peut être fait par un seul développeur en seulement quelques jours. Encore une fois, jetez un oeil à src/adesklets_debug.sh; c'est un traducteur (simplet) du Bourne Shell pour adesklets qui tient dans seulement cinquante lignes, commentaires compris.
Si jamais vous avez besoin d'aide, vous êtes encouragés à écrire (en anglais) à la mailing list adesklets-devel ...
Bonne programmation!
[1] On parle d'environnement de langage parceque le problème n'est pas la syntaxe ou le paradigme utilisé (qu'il soit impératif, fonctionnel ou n'importe quoi d'autre), mais la manière dont vous pouvez gérer des opérations de base de POSIX sur les fichiers, les signaux, etc. (See Fonctionalités requises.). Ainsi un programmeur BASH devrait utiliser adesklets facilement, alors qu'un programmeur Linux JDK devrait probablement recontrer plus de difficultés (sans vouloir critiquer).
[2] Pour adesklets 0.6.1, Python et Perl sont tous deux supportés à la base.
[3] Ceci dit, disposer du support d'IPC et de poll/select rendra les choses à la fois plus simples et plus claires. Il est aussi très utile de pouvoir bloquer des signaux.
[4] Qui serait en fait une interface.
[5] Avec un appel de la famille execve*; voir
man 2 execve
.
[6] Qui sont, nominativement, son écran et ses coordonnées.
[7] Ce qui signifie que la session est interactive
[8] Le caractère '\n' doit être placé après chaque commande.
[9] Ceci permet d'éviter de bloquer le processus en surchargeant le flux avec des commandes arbitrairement longues; bien sur, envoyer trois commandes de quelques centaines d'octets d'un coup n'est pas un problème!
[10] Pour plus de détails sur le format de ces deux fichiers, cf scripting/protoize.sh.in et scripting/enums.sh.
[11] L'idée étant que la plupart des utilisateurs de votre portage doivent être plus ou moins capables de s'en servir avec l'installation de base du langage.
[12] De nos jours éxecuter GNU sed n'est pas un problème sur pratiquement n'importe quel système POSIX.
[13] Tous les paramètres de
MESSAGE
sont séparés par un
esapce.
[14] De toute façon aucun évènement n'est jamais perdu; son apparition est simplement retardée.
[15] Toutes les autres informations sur
stderr
peuvent être ignorées sans
risque.
[16] http://fr.wikipedia.org/wiki/Forme_de_Backus_Naur
[17] man 2 signal
est une
très bonne réference sur les signaux.