Obligement - L'Amiga au maximum

Jeudi 28 mars 2024 - 12:13  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in english


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : C - les commodités
(Article écrit par Olivier Jeannet et extrait d'Amiga News - novembre 1994)


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) :

C

Une commodité est identifiée par son nom, le champ "nb_Name" de la structure NewBroker. Ce champ sert par exemple pour déterminer si on ne lance pas une commodité deux fois.

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 *CxBroker(struct NewBroker *nb, LONG *error)

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 :

LONG ActivateCxObj(CxObj *co, LONG true)

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 :

CxObj *CxFilter(LONG hotkey)

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 :

VOID AffacgCxObj(CxObj *ObjTete, CxObj *co)

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 :

CxObj *CxSender(LONG port, LONG Id)

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 :

CxObj *CxTranslate(LONG ie)

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 :

LONG CxObjError(CxObj *co)

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 :

VOID DeleteCxObj(CxObj *co)

Et :

VOID DeleteCxObjAll(CxObj *co)

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 :

[<class>] {[-][<qualifier>]} [-][upstroke] [<key code>]
  • "class" décrit la classe de l'événement (InputEvent). Ce paramètre est optionnel et en son absence, c'est la classe "rawkey" qui est utilisée par défaut. Voir plus bas les classes d'événements "InputEvent".

  • Les qualificateurs sont des signaux qui doivent être positionnés ou effacés au moment où la touche d'appel est activée. Si vous voulez au contraire ignorer un qualificateur, ajoutez juste un "-" devant son mot-clé. Voir plus bas la liste des qualificateurs.

  • Normalement, un événement touche d'appel est généré quand une touche est enfoncée. Si vous voulez que l'événement soit généré quand la touche est relâchée, ajoutez le mot-clé "upstroke". Quand vous souhaitez générer un événement sur une touche enfoncée ou relâchée, utilisez "-upstroke". Le "key code" (le code d'une touche du clavier) dépend de la classe d'événement choisie. Voir plus bas les codes des touches.

    Note : choisissez vos touches d'appel avec soin car la commodities.library a une priorité haute dans la chaîne du gestionnaire d'événements (c'est-à-dire que vous pourriez prendre le dessus sur une définition existante),

  • Les classes d'événements (InputEvent) :
    • "rawkey", c'est la classe par défaut et elle couvre tous les événements clavier.
    • "rawmouse" : cette classe décrit tous les événements relatifs aux boutons de la souris.
    • "diskinserted" et "diskremoved" : les événements de cette classe sont générés quand une disquette est insérée ou retirée.
Les qualificateurs
  • "lshift" et "rshift" : Shift gauche et droit.
  • "shift" : l'une ou l'autre des touches Shift.
  • "capslock" : la touche Verrouillage Majuscule.
  • "caps" : l'une ou l'autre des touches Shift et de la touche Verrouillage Majuscule.
  • "control : la touche Ctrl (Control).
  • "lalt" et "ralt" : Alt gauche et droite.
  • "alt" : l'une ou l'autre des touches Alt.
  • "lcommand" et "rcommand" : touche amiga gauche et droite.
  • "numericpad" : doit être utilisé pour le pavé numérique.
  • "leftbutton", "midbutton" et "rbutton" : boutons gauche, du milieu et droit de la souris.
Note : la commodities.library V37 (Workbench 2.04) a un bogue qui empêche l'utilisation de "leftbutton", "midbutton" et "rbutton" en tant que qualificateurs. Ce bogue est corrigé dans le version V38 (Workbench 2.1).

Les codes des touches

Chaque classe d'événement a ses propres codes de touches :

1. La classe "rawkey"
  • "a"-"z", "0"-"9", etc. : caractères ASCII.
  • "F1"-"F10" ; touches de fonction.
  • "up", "down", "left", "right", touches des flèches du curseur, "esc", "backspace", "del", "help", "tab", "comma", "return", "space" : touches spéciales.
  • "enter" : touche du pavé numérique (doit être utilisé avec le qualificateur "numericpad" !).
Pour les rawkeys et les qualificateurs, certains synonymes de mots-clés ont été ajoutés à la commodities.library V38, comme "ctrl" pour "control".

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
  • "rall lalt t" : maintenez enfoncées les touches Alt droite et gauche et appuyez sur "t".
  • "rcommand f2" : maintenez enfoncée la touche Amiga droite et appuyez sur la deuxième touche de fonction.
  • "numericpad enter" : appuyez sur la touche "enter" du pavé numérique.
  • "rawmouse midbutton leftbutton mouse_leftpress" : maintenez enfoncé le bouton du milieu de la souris et appuyez sur le bouton gauche de la souris.
  • "diskinserted" : insérez une disquette dans un lecteur.
Comme il a été mentionné plus haut, on peut introduire des événements, par exemple pour effectuer un "coller" après avoir fait un "copier". Les événements sont introduits au début de la liste des brokers. La fonction suivante permet de le faire :

VOID AddIEvents(struct InputEvent *e)

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 :

struct InputEvent *InvertString(STRPTR str, struct KeyMap *km)

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 :

C

Les messages de Commodity

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 :

C

Le troisième argument de "ArgInt()" et "ArgString()" est la valeur par défaut à donner si le deuxième argument n'est pas présent dans l'icône.

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.

C
C
C
C


[Retour en haut] / [Retour aux articles]