Obligement - L'Amiga au maximum

Jeudi 23 novembre 2017 - 10:08  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

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

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


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


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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - Intuition et les gadgets (2e partie)
(Article écrit par Batchman et extrait d'A-News (Amiga News) - janvier 1989)


Après l'exemple du mois dernier, voici les principes, que dis-je, les fondements de la théorie des gadgets. Si vous n'avez pas tout compris dans ce programme, cet article calmera vos angoisses (pour toute information sur les gadgets, voyez cet article).

Notez avant tout cette règle : pour un fonctionnement correct, deux zones de sélection ("select zones") ne doivent jamais se recouvrir, même partiellement. Cet chevauchement peut conduire à toutes sortes de plantages et ne mérite que de figurer au musée du merdiciel.

D'autre part, les gadgets, nous l'avons vu, sont régis par Intuition : il faut donc avant toute opération que votre programme ouvre la bibliothèque correspondante. Ceci se fait en mettant en tête de votre fichier #include <intuition/intuition.h> dans les déclarations struct IntuitionBase *IntuitionBase; et au début du main IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0); à la fin de main CloseLibrary(IntuitionBase);. Nous n'expliquerons pas ceci plus en détail, les bibliothèques étant un sujet beaucoup plus vaste que les gadgets eux-mêmes.

Comment créer un gadget ?

Il y a plusieurs moyens. La façon la plus naturelle est de l'attacher à la liste liée à une fenêtre : quand le programme ouvre la fenêtre, tous les gadgets sont créés. Mais on peut vouloir en ajouter un nouveau à la liste après l'ouverture de la fenêtre : deux solutions sont ici possibles.

Premièrement, on peut modifier les chaînages de la liste pour incorporer une nouvelle structure et appeler la fonction "RefreshGadgets". Cette fonction parcourt toute la liste et rend l'imagerie des gadgets conforme au contenu de chaque structure. Ceci se fait par :

RefreshGadgets(Gadget,Window,Requester)

Avec :
  • Gadget = adresse du premier gadget à rafraîchir.
  • Window = adresse de la fenêtre.
  • Requester = adresse de la fenêtre de requête le cas échéant, NULL sinon.
Deuxième possibilité, qui est meilleure, on appelle la fonction "AddGadget" qui ajoute le gadget à la liste en modifiant elle-même les chaînages. Ceci se fait par :

AddGadget(Window,Gadget,Position)

Avec :
  • Window = adresse de la fenêtre.
  • Gadget = adresse du gadget à ajouter.
  • Position = rang qu'il doit occuper dans la liste.
Cette fonction retourne le rang auquel le gadget a été inséré, qui n'est pas forcément celui qui a été demandé : en effet, si "Position" est supérieur au nombre de gadgets, il est directement placé à la fin de la liste. Si vous souhaitez l'ajouter en fin de liste, le plus simple est donc de poser "Position = -1".

Comment détruire un gadget ?

Laissez tomber la dynamite, la meilleure façon est d'utiliser la fonction "RemoveGadget" qui est le pendant de "AddGadget". Il faut écrire :

RemoveGadget(Window,Gadget)

Avec :
  • Window = adresse de la fenêtre.
  • Gadget = adresse du gadget.
Il faut noter que cette commande ne détruit pas son imagerie : le seul résultat est qu'Intuition ne réagit plus lorsque l'on clique dans la zone de sélection qui est seule détruite.

Comment inactiver un gadget ?

Il se peut que l'on souhaite interdire l'utilisation d'un gadget temporairement et le montrer clairement à l'utilisateur. On peut alors utiliser la fonction "OffGadget" qui le recouvre d'une trame ("ghosted"), le rend inopérant et positionne le drapeau "GADGDISABLED" déjà dans le premier article. Ceci s'obtient par :

OffGadget(Gadget,Window,Requester)

Avec :
  • Gadget = adresse du gadget.
  • Window = adresse de la fenêtre.
  • Requester = adresse de la fenêtre de requête le cas échéant, NULL sinon.
On réactive le gadget grâce à la fonction symétrique "OnGadget" avec la même syntaxe : OnGadget(Gadget,Window,Requester).

Notez cependant que cette fonction ne rétablit pas l'aspect qu'avait le gadget avant d'être recouvert par la trame.

Comment savoir si un gadget est sélectionné ?

Le test consiste à tester si le drapeau "SELECTED" est positionné dans le champ "Flags". Exemple :

if (Gadget.Flags && SELECTED) { /* sélectionné */ }

Mais si une fenêtre contient un grand nombre de gadgets, il n'est pas raisonnable de tous les tester jusqu'à ce que l'un d'entre eux soit sélectionné ; ce qui nous amène à nous poser la question :

Comment savoir qu'un gadget vient de changer d'état ?

Lorsqu'un gadget change d'état, Intuition envoie au programme des messages IDCMP (Intuition's Direct Communication Message Port), en fonction de ceux qui ont été sélectionnés dans la structure "NewWindow".

En effet, chaque fenêtre contient un port (UserPort) qui reçoit des messages en provenance principalement de la souris. Ces messages ne peuvent être que des types sélectionnés dans le champ "IDCMPFlags" de la fenêtre : les seuls qui peuvent être envoyés par un gadget sont GADGETDOWN, GADGETUP et MOUSEMOVE. Votre programme doit donc être capable de les recevoir et de les traiter au fur et à mesure de leur arrivée.

Voyons un peu la signification de ces messages :

CADGETDOWN est le plus important : lorsque vous cliquez sur un gadget (à condition d'avoir mis "GADGIMMEDIATE" dans ses drapeaux), votre programme reçoit un message de cette nature. C'est grâce à lui que voue saurez que l'on a touché à un gadget (obligatoire pour chaque gadget sauf si vous souhaitez un fonctionnement un peu surnaturel).

GADGETUP est moins important mais surtout plus délicat à gérer : votre programme ne reçoit un message de cette nature que, d'une part, si "RELVERIFY" est mis dans le champ "Mass" du gadget et d'autre part, si l'utilisateur après avoir sélectionné le gadget relâche le bouton de la souris à l'intérieur de la zone de sélection. Ceci sert pour les gadgets correspondant à une action importante, qui doit être mûrement réfléchie car elle peut avoir des conséquences graves (exemple : destruction d'un fichier). Dans ce cas, l'utilisateur qui clique un peu trop vite sur ce gadget et se rend compte qu'il est en train de commettre une bourde monumentale a une porte de sortie : il sort le pointeur hors de la zone de sélection et peut alors relâcher le bouton de la souris ; le message GADGETUP n'est pas envoyé. Concrètement, que se passe-t-il ?

Quand il appuie sur le bouton, Intuition envoie "GADGETDOWN" au programme. Celui-ci attend alors "GADGETUP" en testant en même temps le drapeau "SELECTED". Si ce message arrive alors que le gadget est toujours activé, cela signifie que l'utilisateur a relâché le bouton à l'intérieur de la zone de sélection : on peut effectuer l'action associée. Si à un moment le gadget n'est plus sélectionné ("SELECTED" a disparu), le message "GADGETUP" n'arrivera pas car l'utilisateur a renoncé en sortant la souris hors de la zone avant de relâcher. Ce fonctionnement en deux temps équivaut à une demande de confirmation ; l'action ne sera effectuée que lorsque l'utilisateur relâchera le gadget au lieu de l'être des qu'il le sélectionne.

Enfin, le message MOUSEMOVE peut être reçu si vous avez mis le drapeau "FOLLOWMOUSE" pour ce gadget. Il vous avertit que la souris a bougé et il contient les coordonnées de sa nouvelle position. Il peut être utile pour faire varier des effets de couleur ou sonores par exemple. mais cette utilisation est quelque peu contre-nature pour des gadgets booléens (plutôt du type oui-non, ouvert-fermé, etc.) et on l'utilisera plutôt pour des gadgets proportionnels.

Ces messages sont portés (pour faire chic, on dit "encapsulés") par des structures "IntuiMessage". Quand plusieurs messages arrivent, ils sont rangés dans une file (FIFO). On obtient l'adresse de la première structure IntuiMessage de la file grâce à la fonction "GetMsg".

message = (struct IntuiMessage *) GetMsg(Window->UserPort);

Avec :
  • Message : un pointeur sur une structure IntuiMessage. On le déclare par struct IntuiMessage * message;.
  • Window : l'adresse de la fenêtre.
S'il y a des messages en attente, GetMsg retourne l'adresse de la première structure IntuiMessage de la file. Dans le cas contraire, elle retourne NULL. Pour attendre un message, on peut donc écrire :

while (!(message = GetMsg(Window->UserPort)));

Note : votre compilateur risque de protester par un "warning" si vous ne mettez pas (struct IntuiMessage *) : c'est moins bien pour messieurs Kernighan et Ritchie mais ne chipotons pas : c'est correct.

Notez bien aussi que cette écriture n'est pas la meilleure façon d'attendre l'arrivée d'un message car, dans ce cas, le processeur perd son temps à ne rien faire. Dans le cas de programmes susceptibles de tourner en multitâche, il faudra suspendre l'exécution jusqu'à l'arrivée du message : ceci du fait par la fonction "WaitPort", plus compliquée et qui sort du sujet de cet article.

Autre point important : lorsqu'un message arrive, Intuition lui alloue dynamiquement une structure IntuiMessage pour l'ajouter à la file. Une fois que vous avez examiné le message par GetMsg, il faut le retirer de la file (nous ne sommes pas on Pologne) pour libérer la mémoire car sinon, après avoir cliqué quelques milliers de fois sur un gadget (ou plus rapidement avec "FOLLOWMOUSE" qui envoie un message chaque fois que la souris bouge), vous saturerez la mémoire de votre Amiga (au demeurant très vaste mais vous pouvez lui trouver une meilleure utilisation). Une fois que vous en avez fini avec le message, faites donc :

ReplyMsg(message);

Ceci le retirera de la file et libérera la mémoire qu'occupait la structure IntuiMessage. Un dernier mot pour vous mettre en garde sur l'utilisation de cette fonction : votre pointeur message doit obligatoirement contenir l'adresse d'une structure IntuiMessage car si GetMsg a renvoyé NULL et si vous répondez, vous aurez en principe droit à quelque chose de rare sur Amiga : un plantage tout bête, même pas de Guru !

Vous pointez alors un doigt menaçant vers l'article : "Il n'a toujours pas dit comment on peut connaître la nature du message, le fourbe !". Rassurez-vous, c'est très simple. La structure intuiMessage contient tous les renseignements dont vous rêvez :

gadgets

Dans le cadre de cet article, nous n'avons pas besoin de parler d'"ExecMessage", ni de "Code", encore moins de "Qualifier". De mème, je vous suggère de ne pas toucher à "SpecialLink" (d'ailleurs réservé au système) : il s'agit des liens entre les structures IntuiMessage qui constituent la file (à moins que vous ne soyez un amateur inconditionnel de la quatrième dimension ou du rouge clignotant ?).

La nature du message se trouve dans "Class". Exemple :

if (message->Class == GADGETDOWN) { /* traitement */ }

IAddress est aussi très intéressant : ce pointeur contient l'adresse de l'émetteur du message. Par conséquent, pour savoir quel gadget a été sélectionné quand le programme reçoit GADGETDOWN, il y a des solutions :
  • Soit on teste successivement le drapeau "SELECTED" de tous les gadgets jusqu'à ce que l'on trouve celui qui a été activé (bourrin).
  • Soit on compare l'adresse du propriétaire du message à celle de tous les gadgets tant que l'on n'a pas trouvé l'émetteur (mieux).
Par exemple :

if (message->IAddress == &Gadget1)
Procédure1();
else if (message->IAddress == &Gadget2)
Procédure2();
else if ... etc.

Notez au passage une finesse du langage C : vous ne pouvez pas écrire quelque chose du style :

switch (IAddress) {
  case &Gadget1:{...}
  case &Gadget2:{...}...
}

En effet, les valeurs suivant une case doivent être des constantes. Or, par définition, sur l'Amiga, une adresse ne peut être considérée comme une constante, les programmes étant relogeables. Il faut donc écrire une cascade de "else if'. C'est dommage car un "switch" aurait été non seulement plus joli (plus facile à lire) mais aussi et surtout plus efficace du point de vue du code généré par le compilateur (plus rapide et prenant moins de place).

Eh bien, figurez-vous que c'est possible ; voici la bonne méthode : Il faut pour cela utiliser le champ "GadgetID" qui vous est réservé dans la structure "Gadget". Placez dans ce champ une valeur différente pour chaque gadget (exemple : son numéro). Après avoir récupéré cette valeur, votre programme pourra effectuer un "switch". Exemple :

Propri = ((struct Gadget *) message->IAddress)->GadgetID;
switch (Propri) { /* etc. */

Mais ne nous égarons pas, la structure IntuiMessage recèle encore quelques trésors que je meurs d'envie de vous livrer.

"MouseX" et "MouseY" contiennent les coordonnées de la souris par rapport à l'origine de la fenêtre lors de l'envoi du message. Mieux que le cachet de la poste pour situer le moment. "Seconds" et "Micros" contiennent l'heure exacte de l'envoi du message (et pas avec un mois de retard, l'Amiga ignorant généralement les grèves). "Micros" contient le nombre de microsecondes écoulées depuis le début de la seconde où s'est produit l'événement. "Seconds" contient pour sa part le nombre de secondes écoulées depuis la mise en route de la machine, modulo 139 ans (il y a de la marge).

Enfin, le pointeur "IDCMPWindow" vous donne l'adresse de la fenêtre contenant le gadget qui a émis le message. C'est utile si vous avez en même temps à l'écran plusieurs fenêtres contenant des gadgets.

Un dernier mot pour vous dire que vous pouvez, quand vous le voulez, changer la sélection de messages que peut envoyer la fenêtre grâce à la fonction "ModifyIDCMP" avec :

ModifyIDCMP(Window,IDCMPFlags);

Avec :
  • Window : pointeur sur la fenêtre.
  • IDCMPFlags : les nouveaux drapeaux, NULL si la fenêtre ne doit plus envoyer de messages.
Voilà

Vous connaissez maintenant tout ce qu'il faut pour utiliser les gadgets booléens dans un programme. A vous d'adapter à vos besoins l'exemple du mois dernier et n'hésitez pas à faire part à A-News de vos trouvailles ou de vos questions ; ou laissez-moi vos messages sur 3615 DEEP bal COING ou BATCHMAN.


[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]