Obligement - L'Amiga au maximum

Lundi 24 juillet 2017 - 18:39  

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 : Assembleur - input.device
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - janvier 1996)


Tout d'abord, je me permets de vous souhaiter une bonne année, riche en programmation, sur Amiga bien sûr. Ce mois-ci, nous laissons AmigaGuide de côté et nous abordons un des piliers centraux du système, plus par son rôle que par sa complexité ; j'ai nommé l'input.device.

Présentation

L'input.device est un périphérique Exec se trouvant en ROM. Son rôle est de gérer une chaîne centralisant tous les événements agissant sur l'environnement de l'Amiga : touches du clavier, déplacement de la souris, insertion d'une disquette, mais aussi activation d'une fenêtre ou choix d'un menu. Ces événements sont "distribués" tour à tour aux applications qui en font la demande et celles-ci peuvent y réagir.

Ainsi, pour attendre que l'utilisateur clique sur le bouton de la souris, on ne pioche pas directement dans les registres matériels comme on le voit encore trop souvent, mais on demande à l'input.device de nous prévenir quand cela arrive. Cette organisation permet une grande souplesse, indispensable d'ailleurs à tout système d'exploitation qui se respecte (suivez mon regard) : on peut non seulement imaginer de nouveaux types d'événements, mais ceux-ci peuvent être pris en compte par un nombre quelconque d'applications à la fois.

On comprend alors comment fonctionne le système : lorsque vous appuyez sur le bouton gauche de la souris, cela est détecté par le gameport.device, qui injecte un événement dans l'input.device. Cet événement est alors détecté (entre autres) par l'intuition.library qui teste la position du pointeur de souris. Si par exemple celui-ci se trouve au-dessus du gadget de fermeture d'une fenêtre, Intuition produit un nouvel événement, de type fermeture de fenêtre cette fois, et l'injecte dans l'input.device. Celui-ci est alors reçu, "au tour suivant" par les différents gestionnaires, dont... celui d'Intuition, c'est-à-dire celui qui a envoyé cet événement ! A la réception de celui-ci, Intuition détermine si le programme possédant cette fenêtre a demandé à recevoir les messages IDCMP (eh oui, voilà leur origine) de type IDCMP_CLOSEWINDOW.

Si c'est le cas, un message IDCMP lui est envoyé et l'événement est retiré de la chaîne. Si ce n'est pas le cas, l'événement poursuit sa route et peut alors être intercepté par le console.device (cas des fenêtres Shell de type CLOSE par exemple) ou d'autres personnes, ce qui n'aurait pas été possible si Intuition avait réagi "directement" sans repasser par l'input.device.

La structure INPUTEVENT

L'input.device possède sa propre tâche, originalement nommée input.device, mais attention, ce n'est pas un processus (donc pas question d'appeler le DOS !). Les événements gérés par l'input.device sont structurés en InputEvents, qu'on retrouve d'ailleurs un peu partout, depuis certains paramètres fournis à Intuition à ceux utilisés par la commodities.library. La structure InputEvent est définie dans le fichier d'inclusion "devices/inputevent.(h|i)" :

struct InputEvent {
    struct      InputEvent *ie_NextEvent;
    UBYTE       ie_Class;
    UBYTE       ie_SubClass;
    UWORD       ie_Code;
    UWORD       ie_Qualifier;
    union {
    struct {
        WORD    ie_x;
        WORD    ie_y;
    } ie_xy;
    APTR    ie_addr;
    struct {
        UBYTE   ie_prev1DownCode;
        UBYTE   ie_prev1DownQual;
        UBYTE   ie_prev2DownCode;
        UBYTE   ie_prev2DownQual;
    } ie_dead;
    } ie_position;
    struct timeval ie_TimeStamp;
};
#define ie_X                ie_position.ie_xy.ie_x
#define ie_Y                ie_position.ie_xy.ie_y
#define ie_EventAddress     ie_position.ie_addr
#define ie_Prev1DownCode    ie_position.ie_dead.ie_prev1DownCode
#define ie_Prev1DownQual    ie_position.ie_dead.ie_prev1DownQual
#define ie_Prev2DownCode    ie_position.ie_dead.ie_prev2DownCode
#define ie_Prev2DownQual    ie_position.ie_dead.ie_prev2DownQual

Ne vous laissez pas impressionner par sa complexité car elle n'est qu'apparente. Le champ ie_NextEvent pointe sur la structure InputEvent suivante (elles sont toujours chaînées par ordre chronologique, bien entendu), l'octet ie_Class indique la classe de l'événement. Les valeurs possibles sont définies dans le fichier d'inclusion, elles vont de l'appui sur une touche IECLASS_RAWKEY à la modification de la position et de la taille d'une fenêtre IECLASS_CHANGEWINDOW. Le champ ie_SubClass peut servir à préciser ce type, ses valeurs possibles dépendant entièrement de ie_Class. ie_Code contient un code spécifique, par exemple le code de la touche sur laquelle vous avez appuyé. ie_Qualifier contient toujours l'état des touches qualificateurs (Shift, Alt, Amiga, Ctrl, Caps Lock), mais aussi d'autres valeurs plus inattendues et fort utiles comme REPEAT (cas d'une touche répétée) ou NUMERICPAD pour le pavé numérique.

Vient ensuite une grosse union. C'est la façon compliquée du C de dire que les données à cet endroit dépendent de la classe de l'événement. Selon cette classe, on peut y trouver les coordonnées du pointeur de la souris, les codes des touches mortes (accent circonflexe, tréma) pressées auparavant, l'adresse d'une structure plus grande... Rien de bien méchant.

Enfin, le champ ie_TimeStamp contient la date à laquelle l'événement s'est produit. C'est utile pour tester un double-clic par exemple.

Le périphérique lui-même

Comme je l'ai dit plus haut, le périphérique est écrit autour d'une tâche, qui gère une chaîne d'InputEvents. Elle gère aussi une liste de "clients", les InputHandlers. Il s'agit de routines d'applications qui se sont déclarées auprès de l'input.device afin de recevoir les InputEvents. Il y en a trois en standard dans l'Amiga : celui d'Intuition, celui de la commodities.library et celui du console.device.

L'ouverture du périphérique se fait comme d'habitude par la fonction OpenDevice(), il n'y a rien de particulier à ce sujet (les paramètres unité et drapeaux (flags) sont à mettre à 0). Signalons tout de même que l'input.device utilise une structure IOStdReq. Voyons donc les commandes intéressantes de l'input.device :
  • IND_WRITEEVENT : insère un nouvel événement dans la chaîne. Celui-ci sera traité par tous les clients. Le champ io_Length doit contenir la taille de la structure InputEvent, et io_Data pointe sur la structure InputEvent elle-même. Dans celle-ci, le champ ie_NextEvent sera ignoré et la structure TimeStamp sera mise à l'heure courante (uniquement à partir du Kickstart 2.0). Il est à noter que le contenu de cette structure est détruit.

  • IND_ADDHANDLER : cette commande ajoute un nouveau gestionnaire à la liste de l'input.device. Il est décrit par une structure Interrupt pointée par le champ io_Data de la structure IOStdReq. La priorité (champ ln_Pri) doit être initialisée, elle conditionne l'ordre dans lequel les gestionnaires sont appelés. Le gestionnaire d'Intuition a une priorité de 50, celui du console.device une priorité de 0. La routine pointée par le champ is_Code de la structure Interrupt est appelée à chaque fois qu'un nouvel événement apparaît. Dans ce cas, le registre A0 contient l'adresse du premier événement, le chaînage avec les autres étant fait par le champ ie_NextEvent. Le registre A1 contiendra alors ce que vous avez cru bon de mettre dans le champ is_Data de la structure Interrupt. En retour, la fonction doit retourner (en D0) l'adresse des structures InputEvent qui remplaceront celles reçues en paramètre.

  • IND_REMHANDLER : vous vous en doutiez, cette commande retire la structure Interrupt ajoutée par IND_ADDHANDLER de la liste du périphérique. La documentation officielle précise que cette commande n'est pas immédiate (le drapeau IOF_QUICK sera alors mis à 0), sans doute à cause du fait qu'il faut que ce soit la tâche du périphérique (qui peut être occupée à autre chose) qui enlève la structure. IND_ADDHANDLER n'est d'ailleurs pas immédiate non plus.
Un exemple

Après toutes ces explications théoriques, nous allons voir un exemple simple d'utilisation de l'input.device. Il utilise une classe qui a été introduite avec la version 36 (Kickstart 2.0) : IECLASS_NEWPOINTERPOS. Elle permet (notamment) le positionnement du pointeur de souris à un endroit précis d'un écran Intuition. Cet événement est d'ailleurs pris en charge par l'InputHandler d'Intuition. Dans le cas de cette classe, le champ ie_EventAddress contient un pointeur sur une structure IEPointerPixel.

Je vous renvoie aux commentaires du listage pour de plus amples informations, en précisant que ce programme prend en argument les coordonnées du pointeur en pixels et le nom d'un écran public (facultatif), par exemple: 200 200 DEVPAC.1.

    include exec/exec.i
    include exec/exec_lib.i
    include intuition/intuition_lib.i
    include devices/input.i
    include devices/inputevent.i
    include dos/dos_lib.i

    move.l  4.w,a6
    moveq   #20,d7  ; code de retour: FAIL
    move.l  a6,Exec.Base
    lea     DOS.Name(pc),a1 ; ouvre le DOS
    moveq   #37,d0          ; Kickstart 2.04 minimum
    jsr     _LVOOpenLibrary(a6)
    move.l  d0,DOS.Base
    beq     exit
    lea     Intuition.Name(pc),a1   ; ouvre Intuition
    moveq   #37,d0
    jsr     _LVOOpenLibrary(a6)
    move.l  d0,Intuition.Base
    beq     CloseDOS
    jsr     _LVOCreateMsgPort(a6)   ; crée un port
    move.l  d0,Input.Port       ; de réponse
    move.l  d0,a0
    moveq   #IOSTD_SIZE,d0  ; crée une struct
    jsr     _LVOCreateIORequest(a6) ; IOStdReq
    move.l  d0,Input.IO
    beq     FreeIO
    move.l  d0,a1
    lea     Input.Name(pc),a0
    moveq   #0,d0
    move.l  d0,d1   ; ouvre l'input.device
    jsr     _LVOOpenDevice(a6)
    tst.b   d0
    bne     FreeIO

    move.l  #Args.Template,d1   ; lit les arguments
    move.l  #Args.Array,d2
    moveq   #0,d3
    move.l  DOS.Base(pc),a6
    jsr     _LVOReadArgs(a6)
    move.l  d0,Args.RDArgs
    beq.s   CloseInput

    moveq   #10,d7  ; code de retour: ERROR
    lea     Input.Event(pc),a3  ; struct InputEvent
    lea     IE.PointerPixel(pc),a4  ; struct IEPointerPixel
    move.b  #IECLASS_NEWPOINTERPOS,ie_Class(a3) ; classe
    move.b  #IESUBCLASS_PIXEL,ie_SubClass(a3)   ; sous-classe
    move.l  a4,ie_EventAddress(a3)  ; lie les 2 structs

    move.l  PubScreen.Name(pc),a0   ; verrouille l'écran
    move.l  Intuition.Base(pc),a6   ; public demandé
    jsr     _LVOLockPubScreen(a6)
    move.l  d0,iepp_Screen(a4)
    beq.s   FreeArgs
    move.l  d0,a0
    jsr     _LVOScreenToFront(a6)   ; l'amène en avant
    move.l  Pointer.X(pc),a0    ; position du pointeur
    move.w  2(a0),iepp_PositionX(a4)
    move.l  Pointer.Y(pc),a0
    move.w  2(a0),iepp_PositionY(a4)

    move.l  Input.IO(pc),a1
    move.l  a3,IO_DATA(a1)  ; struct InputEvent à ajouter
    move.l  #ie_SIZEOF,IO_LENGTH(a1)    
    move.w  #IND_WRITEEVENT,IO_COMMAND(a1)  ; commande: écrire
    move.l  Exec.Base(pc),a6
    jsr     _LVODoIO(a6)
    moveq   #0,d7   ; code de retour: OK

UnlockScreen
    suba.l  a0,a0   ; déverrouille l'écran
    move.l  iepp_Screen(a4),a1
    move.l  Intuition.Base(pc),a6
    jsr     _LVOUnlockPubScreen(a6)
FreeArgs
    move.l  Args.RDArgs(pc),d1  ; libère les arguments
    move.l  DOS.Base(pc),a6
    jsr     _LVOFreeArgs(a6)
CloseInput
    move.l  Input.IO(pc),a1 ; ferme l'input.device
    move.l  Exec.Base(pc),a6
    jsr     _LVOCloseDevice(a6)
FreeIO      move.l  Input.IO(pc),a0 ; peut être nul
    jsr     _LVODeleteIORequest(a6) ; libère l'IOStdReq
    move.l  Input.Port(pc),a0   ; libère le port
    jsr     _LVODeleteMsgPort(a6)
CloseIntuition
    move.l  Intuition.Base(pc),a1   ; ferme Intuition
    jsr     _LVOCloseLibrary(a6)
CloseDOS
    move.l  DOS.Base(pc),a1 ; ferme le DOS
    jsr     _LVOCloseLibrary(a6)
exit move.l d7,d0
    rts

Exec.Base       dc.l    0
DOS.Base        dc.l    0
Intuition.Base  dc.l    0
Input.Port      dc.l    0
Input.IO        dc.l    0

Args.Array
Pointer.X       dc.l    0
Pointer.Y       dc.l    0
PubScreen.Name  dc.l    0

Args.RDArgs     dc.l    0

Input.Event     dcb.b   ie_SIZEOF,0
IE.PointerPixel dcb.b   IEPointerPixel_SIZEOF,0

DOS.Name        dc.b    'dos.library',0
Intuition.Name  dc.b    'intuition.library',0
Args.Template   dc.b    'X/N/A,Y/N/A,PUBSCREEN',0
Input.Name      dc.b    'input.device',0


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