|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Les commodités permettent d'utiliser les combinaisons de touches spéciales (par exemple "Control+Alt+F1") et les événements clavier/souris en général, de façon propre et aisée, grâce à la bibliothèque commodities.library. Il existe de nombreux petits programmes, le plus souvent des utilitaires du domaine public, destinés à nous faciliter la vie avec le Workbench. On trouve dans ce type de programme les possibilités suivantes : économiseur d'écran, activateur de fenêtre, lanceur de Shell via "hotkey" (combinaison de touches), minimisation/maximisation de la taille des fenêtres par touche de fonction, copier/coller n'importe où à l'écran, menus contectuels, etc. Les deux premiers exemples sont livrés avec le système 2.0 (dans le tiroir Tools/Commodities) et s'appellent respectivement Blanker et AutoPoint. D'autres utilitaires sont également fournis dans ce répertoire. Le sujet qui nous intéresse dans cet article est la programmation des "hotkeys" ainsi que la facilité d'emploi des utilitaires qui se servent de la commodities.library. Rappelons quelle était la situation avec le système 1.3 (avant le 2.0). La situation avant le 2.0 Parmi les premiers utilitaires, on trouve DMouse (logiciel DP par Matt Dillon), qui permet, entre autres, d'accélérer la souris, d'activer la fenêtre sous le pointeur et de lancer un Shell par une combinaison de touches. Pour cela, il faut intercepter les événements clavier et souris. On programme cette possibilité en ajoutant un "input-handler" (gestionnaire d'événements) dans la chaîne de traitement des événements d'Intuition. Il s'agit d'un petit programme qui va inspecter les valeurs contenues dans les champs de la structure InputEvent. L'inconvénient est qu'il faut en général recourir à l'assembleur car on reçoit les événements (structure InputEvent), dans un des registres d'adresse du processeur, au lieu du registre de données D0. Les compilateurs modernes permettent d'utiliser un registre précis comme argument, mais cela n'a pas toujours été le cas. Autre inconvénient de cette méthode, la détection des touches d'appel. Les valeurs présentes dans la structure InputEvent sont des codes clavier, en particulier pour les touches spéciales (Control, Shift, etc.). Pour détecter la combinaison "Control+Alt+a", il faut connaître les codes de ces touches. Pour compliquer les choses, dans la combinaison citée, "Control" et "Alt" sont des qualificateurs ("qualifiers"). Il faut donc détecter les qualificateurs dans le champ "ie_Qualifier" ainsi que la touche pressée ("a" ici), dans le champ "ie_Code" de la structure. A l'inverse, pour générer des événements (comme pour insérer des caractères dans un Shell), il faut allouer des structures InputEvent et les remplir correctement. Dernier inconvénient du Workbench 1.3, on ne sait pas très bien quels sont les utilitaires qui tournent en mémoire, ni les touches d'appel utilisées. Les progrès du 2.0 Après avoir vu les difficultés de la programmation des touches d'appel avec le système 1.3, voyons à présent les améliorations apportées par le système 2.0. Tous ceux qui utilisent un Workbench 2.0 ou plus connaissent le programme Exchange (ou Commodities Exchange), et ceux qui sont restés en 1.3 ont du en entendre parler. Ce programme recense les utilitaires (en train de tourner) qui font appel à la commodities.library pour se déclarer au système. On appelle alors "commodités" ces utilitaires. Le programme Exchange, qui est lui-même une commodité, permet de rendre active ou inactive une commodité, de la "tuer" (le programme se termine) et de monter ou cacher l'interface de la commodité, si elle en possède une. Un programme qui se déclare comme commodité reçoit des messages par un port de communication (MsgPort). C'est par un port de communication que la commodité reçoit les messages d'Exchange comme "montrer" ou "tuer". On voit là un des avantages de la commodité : si on a oublié la touche d'appel d'un programme (par exemple "AmigaGauche+AmigaDroite+x" pour Xoper), on peut l'appeler en utilisant "montrer" dans Exchange. Ceci est possible grâce à la commodities.library. On voit d'ailleurs apparaître des clones d'Exchange comme CXHandler qui permet d'utiliser le clavier en plus de la souris. Seul problème : quand on ne le laisse pas ouvert en permanence, il faut se souvenir de la touche d'appel d'Exchange... Exchange s'appelle par défaut avec "Control+Alt+Help" en 3.0 et "Alt+Help" en 2.0 (si je me souviens bien). Nous venons de voir les facilités apportées à l'utilisateur. Voyons à présent celles qui concernent le programmeur. Nous avons parlé de la difficulté à détecter les touches d'appel, du fait des codes clavier à connaître. Là encore, la commodities.library nous facilite la tâche. Il n'y a plus besoin d'ajouter un input-handler, il suffit de demander d'être informé si une combinaison est pressée, et le programmeur se contente d'indiquer en toutes lettres la combinaison (comme "Alt+Help"), dans l'appel de la fonction. Nous détaillerons cette fonction plus loin. Il paraît complètement inutile, voire néfaste, de lancer deux fois la même commodité : une seule suffit. C'est dans cette optique qu'il est prévu de pouvoir empêcher qu'une commodité soit lancée une deuxième fois. La demande de création de la deuxième commodité sera rejetée. La commodité qui tourne déjà peut également être informée qu'on a voulu la lancer à nouveau. Cette information a en général l'utilité suivante : si la commodité possède une fenêtre, elle l'ouvre (ou ramène au premier plan) : dans le cas contraire, elle se termine (c'est une manière pratique de quitter une commodité qui tourne sans interaction avec l'utilisateur). Définition du "broker" Pour déclarer une commodité (un broker), il faut remplir une structure NewBroker, dont voici le détail (tiré du fichier include "libraries/commodities.h) : On peut donner deux drapeaux pour le champ "nb_Unique" : "NBU_UNIQUE" pour interdire l'exécution en double, "NBU_NOTIFY" pour être informé de la tentative de double exécution. On met la valeur "COF_SHOW_HTDE" dans le champ "nb_Flags" pour préciser que le programme a une fenêtre : Exchange permet alors de cliquer sur les boutons montrer/cacher. Le champ "nb_Pri" précise la priorité avec laquelle le nouveau broker sera inséré dans la liste des commodités (vers le début ou vers la fin). Il est utile de pouvoir préciser cette priorité dans les types d'outils (ligne "CX_PRIORITY") de l'icône de la commodité, comme avec celles livrées avec le Workbench. Le port de communication sert à recevoir les messages du système : pour ce faire, il est nécessaire d'en créer un. La fonction "CxBroker" de la commodities.library sert à déclarer une commodité :
CxObj désigne un objet des commodités. On obtient un broker (courtier, littéralement) en cas de réussite. "NULL" sinon. Le paramètre "error" est soit "NULL", soit un pointeur vers un "LONG" qui recevra un code d'erreur. En pratique, on peut mettre error à "NULL". Tous les objets commodités ont un état d'activation. Quand l'objet est actif, il effectue son action quand un message de la commodities.library (de type CxMessage) arrive. Quand il est inactif, il ne reçoit pas de messages. Tous les objets sont créés dans l'état actif, à l'exception des brokers. Une fois que le broker a été créé, avec éventuellement des objets rattachés (voir plus loin), il apparaît dans la liste des commodités, mais il est inactif. Il faut donc l'activer avec fonction suivante :
L'argument "co" est l'objet commodité (notre broker en l'occurrence), "true" est à 0 pour désactiver l'objet (différent de 0 pour activer), et le résultat est l'état d'activation précédent (0 pour inactif). Détecter une touche d'appel En 2.0, il n'y a plus besoin d'ajouter d'input-handler car la commodities.library met en place son propre gestionnaire d'événements, et envoie à la liste des commodités les touches d'appel qui les concernent. Pour être informé de l'utilisation d'une touche d'appel, la méthode la plus pratique consiste à mettre en place un filtre. On utilise pour cela la fonction "CxFilter()", une macro définie dans "commodities.h" qui crée un CxObjet de type filtre. Ce filtre retient les Input Events (événements) qui correspondent à la chaîne de la touche d'appel. La syntaxe est :
En fait, le paramètre "hotkey" est (un pointeur sur) une chaîne de caractères. Nous verrons plus loin comment définir une touche d'appel. Il faut ensuite attacher ce filtre à notre broker avec la fonction suivante :
Cette fonction attache un objet Commodity à la fin d'une liste d'objets. Pour que ce filtre nous informe de l'arrivée de la touche d'appel, il faut créer un CxObjet de type Sender (émetteur). On utilise dans ce cas la macro :
L'argument "port" est notre MsgPort (port de communication). "Id" est l'identité du CxMessage que l'émetteur envoie au MsgPort. Cette identité est un nombre que l'on définit dans le programme, il sert à différencier les CxMessage qu'on peut recevoir dans le cas où on crée plusieurs émetteurs, chacun émettant pour une touche d'appel différente. Il faut attacher ce CxObjet à notre filtre, auquel il sert d'émetteur. Le pointeur de données associé au CxMessage pointera vers une copie de la structure InputEvent (associée au CxMessage original). Il n'est pu souhaitable que la touche d'appel arrive jusqu'à Intuition, il vaut mieux "l'absorber" pour que le reste du système l'ignore. Pour cela, on a besoin d'un CxObjet (encore un !) de type "Translate" (traducteur), La macro a la syntaxe suivante :
Quand un CxObj Translate reçoit un "CxMessage", il efface le message original et le remplace par un nouvel événement dans la chaîne des événements, après le gestionnaire d'événements de Commodities. L'argument "ie" pointe vers une structure InputEvent, source de notre nouvel événement. Pour absorber la touche d'appel, il suffit de donner "NULL" et aucun nouvel événement ne sera introduit. Ainsi, tout événement arrivant à l'objet "Translate" disparaîtra de la chaîne des événements. On attache également cet objet à notre filtre. Pour finir, on peut vérifier que tout s'est bien passé en utilisant la fonction suivante :
Quand une fonction agissant sur un objet échoue, elle enregistre l'échec dans l'objet. Plusieurs erreurs peuvent être accumulées car chaque type est défini par un bit précis (dans "commodities.h"). La fonction retourne zéro en cas de succès. En résumé, pour détecter une touche d'appel, une fois le broker créé, il faut : créer un filtre (avec CxFilter()), l'attacher au broker, créer un émetteur (avec CxSender()), l'attacher au filtre, créer un traducteur (avec CxTranslate()), et l'attacher au filtre. La méthode pourrait sans doute être simplifiée, mais elle a l'avantage d'être entièrement paramétrable. On dispose également de la possibilité d'accéder directement aux événements en utilisant un objet de type "Custom". Dans ce cas, il faut faire attention car on travaillera directement sur les événements, au sein du gestionnaire. On peut extraire ainsi informations de position de souris. En quittant le programme, il faut veiller à libérer tout ce qui a été alloué. On dispose de deux fonctions pour effacer les CxObjets créés :
Et :
La première efface un CxObjet, la seconde efface récursivement un arbre de CxObjets. Le CxObjet "co" est bien entendu invalide après l'appel de la fonction. Comme il est peu souhaitable d'effacer un objet à qui d'autres objets sont rattachés (par exemple pour un filtre), on utilisera dans ce cas "DeleteCxObjAll()". Il est à noter qu'une commodité bien écrite devrait pouvoir se terminer en recevant un signal "Ctrl-C" (envoyé par la commande "Break" par exemple), ce qui peut être utile. Spécifier une touche d'appel La définition des touches d'appel est expliquée dans la documentation de Yak (logiciel du DP), dont voici un résumé. La touche d'appel est une chaîne de description qui est analysée par la commodities.library. Tous les mots-clés peuvent être écrits différemment en majuscules ou minuscules. Chaque fois qu'une touche d'appel est activée, la commodities.library génère un événement qui est utilisé par les commodités actives. Voici la syntaxe utilisée pour la chaîne de description d'une touche d'appel :
Les codes des touches Chaque classe d'événement a ses propres codes de touches : 1. La classe "rawkey"
2. La classe "rawmouse" Ces mots-clés ont été ajoutés à la commodities.library V38. Ils ne sont donc pas disponibles dans la V37 : "mouse_leftpress", "mouse_middlepress", "mouse_rightpress", presser le bouton gauche/milieu/droit de la souris. Note : pour utiliser un de ces codes, vous devez aussi mettre le mot-clé du qualificateur correspondant, par exemple : "rawmouse leftbutton mouse_leftpress". Exemples de touches d'appel
L'argument "ie" est une liste chaînée d'événements. La fonction de support (présente dans amiga.lib) appelée InvertString() nous simplifie les choses. La syntaxe en est :
Cette fonction génère la liste d'événements correspondant à la chaîne "str", en utilisant le clavier défini par "km" (NULL=défaut). La chaîne peut contenir des descriptions de touches spéciales (comme "Alt F1") ou des caractères spéciaux comme "Return" (avec "/n"). Elle inverse au passage la chaîne, il faut après usage libérer la mémoire allouée pour la liste chaînée. On procède ainsi au total : Avant d'écrire notre programme, il reste à voir les types de CxMessage que l'on peut recevoir. Ils sont au nombre de deux : CXM_IEVENT (événement) et CXM_COMMAND (commande). On extrait le type du message avec la fonction "CxMsgType()", le champ "Id" est donné par "CxMsgID()". Les messages "CXM IEVENT" circulent dans le système d'événements, et sont envoyés par un objet "Sender" (émetteur). Le champ "Id" donne la nature du message, d'après la valeur qui a été donnée en créant l'objet. La section donnée du message pointe vers l'événement qui a déclenché le message. Les messages "CXM_COMMAND" sont envoyés par le programme de contrôle (autrement dit, Exchange) au MsgPort attaché au broker. Le champ "Id" donne dans ce cas la nature de la commande. Les valeurs suivantes ont été définies : "CXCMD_DISABLE" et "CXCMD_ENABLE" (inactivation et activation), CXCMD_APPEAR" et "CXCMD_DISAPPEAR" (montrer et cacher), CXCMD_KILL" (quitter), "CXCMD_LIST_CHG" (la liste des commodités a changé), "CXCMD_UNIQUE" (on a tenté de lancer une commodité avec le même nom). Le programme Le programme qui suit montre une simple fenêtre quand la touche d'appel est enfoncée, et obéit aux ordres envoyés par Exchange. Il se termine également en recevant un signal "Ctrl-C". La touche d'appel s'affiche dans le titre de la fenêtre (c'est recommandé et pratique). Une ultime information avant le listing : la bibliothèque de support amigalib offre une technique très simple pour extraire les valeurs spécifiées dans les types d'outils des icônes. L'icon.library doit être ouverte, voici le déroulement : En conclusion Une fois de plus, le système 2.0 a apporté de nombreuses améliorations au programmeur, et à l'utilisateur également dans le cas présent (avec Exchange). La manipulation des événements (détection et émission) devient presque triviale, et on obtient une vision d'ensemble des utilitaires présents en mémoire.
|