Obligement - L'Amiga au maximum

Mercredi 24 avril 2024 - 04:15  

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 : Assembleur - exemple d'InputHandler
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - février 1996)


Le mois dernier, nous avons vu comment ajouter des éléments dans la chaîne gérée par l'input.device. Nous approfondissons cette fois avec un exemple d'InputHandler appelé par l'input.device pour la réception de tels événements. J'en profiterai pour vous décrire rapidement chaque classe d'événements possibles.

Rappels

Les InputHandlers sont des routines installées par des applications dans l'input.device et appelées tour à tour par ordre de priorité par celui-ci, afin de recevoir des événements représentés par des structures InputEvent. Ils sont décrits par une structure Interrupt et déclarés auprès de l'input.device par sa commande IND_ADDHANDLER, retirés par IND_REMHANDLER.

Chaque InputHandler reçoit en paramètre en A0 une liste (à chaînage simple par le champ ie_NextEvent) de structures InputEvent et en A1 le contenu du champ is_Data de la structure Interrupt. En D0, il doit retourner un pointeur sur la chaîne des événements qui remplace celle reçue. Un point de détail n'est pas précisé dans la documentation officielle : comment allouer et libérer de telles nouvelles structures InputEvent depuis l'intérieur de l'InputHandler ? En effet, il n'y a pas de problème pour "avaler" les structures InputEvent reçues (il suffit de les retirer du chaînage). On peut également modifier sans risque les structures originales (prudence quand même !), mais si de nouvelles structures sont réellement nécessaires, il faut bien les allouer ! L'incertitude est alors de savoir à quel moment la mémoire qu'elles occupent peut être libérée car, n'oublions pas, l'InputHandler est appelé par la tâche de l'input.device et non celle qui l'a installé !

Il semble raisonnable de penser que ces structures peuvent être libérées à l'appel suivant de l'InputHandler (sur la tâche de l'input.device dans ce cas), ou, à défaut, après que celui-ci ait été retiré par la commande IND_REMHANDLER (la libération se fait alors par la tâche qui a retiré l'InputHandler) : sans connaître l'implémentation exacte de l'input.device, on peut supposer que celles-ci ne font le trajet qu'une fois... Cela implique donc de garder une trace des structures qu'on a allouées dans une liste qui est vidée par une routine dédiée. Notons à ce propos que c'est une mauvaise idée que de se dire qu'on ne les libère que lorsque la commande IND_REMHANDLER est exécutée car un InputHandler peut être appelé un grand nombre de fois (imaginez lorsque vous faites quelques kilomètres avec la souris) et la mémoire pourrait rapidement être saturée. De plus, entre les deux tours, attendez-vous à la possibilité que vos structures aient été modifiées par un autre InputHandler.

Les classes

Par ordre numérique, voici les classes reconnues par l'input.device à ce jour (Kickstart 3.1). Elles ont toutes le préfixe IECLASS_ et sont définies dans le fichier d'inclusion devices/inputevent.i :

NULL (0) : à ignorer (?).

RAWKEY (1) : généré par le keyboard.device (en général...), ceci indique un événement en provenance du clavier comme un appui ou relâchement d'une touche. Le champ ie_Code contient le code de la touche en question, éventuellement avec le préfixe IECODE_UP_PREFIX, indiquant si la touche est enfoncée ou relâchée.

RAWMOUSE (2) : généré par le gameport.device. Cela indique un déplacement de la souris. Le champ ie_xy contient la position de la souris (relative ou absolue selon l'état du qualificateur IEQUALIFIERF_RELATIVEMOUSE) et le champ ie_Code si un bouton a été enfoncé ou relâché (toujours en utilisant le préfixe IECODE_UP_PREFIX). Il est à noter que les boutons de la souris sont aussi considérés comme qualificateurs (et se retrouvent donc dans le champ ie_Qualifier) mais que seules leurs transitions se retrouvent dans ie_Code.

EVENT (3) : concerne les opérations de fenêtres comme changement de taille ou rafraîchissement (lorsqu'elles ne sont pas interceptées par l'IDCMP). Ces événements sont destinés au gestionnaire de console sur Kickstart 2.0 et supérieurs. Les conditions dans lesquelles sont générés de tels événements sont assez obscures.

POINTERPOS (4) : rapporte juste les nouvelles coordonnées de la souris. Il dérive d'un événement RAWMOUSE qui a été reçu et traité par Intuition.

TIMER (6) : événement généré par une temporisation. Ces événements sont traduits si nécessaire par Intuition en IDCMP_INTUITICKS.

GADGETDOWN (7) et GADGETUP (8) : sont générés par Intuition (qui a avalé les RAWMOUSE initiaux) lorsque le bouton gauche de la souris a été respectivement enfoncé et relâché au-dessus d'un gadget. GADGETUP n'est généré que si le gadget est le même que celui concerné par le GADGETDOWN précédent (tout comme IDCMP_GADGETDOWN et IDCMP_GADGETUP !). L'adresse de la structure Gadget se trouve dans le champ ie_EventAddress.

REQUESTER (9) : indique qu'une requête (Intuition) a été ouverte ou fermée dans une fenêtre. Ces états sont indiqués respectivement par la présence de IECODE_REQSET et IECODE_REQCLEAR dans le champ ie_Code. L'adresse de la structure Requester est dans le champ ie_EventAddress.

MENULIST (10) : indique que l'utilisateur a sélectionné un menu. Le code de celui-ci se trouve dans le champ ie_Code, il s'agit d'un MenuNumber tels que ceux acceptés par la fonction ItemAddress() d'Intuition.

CLOSEWINDOW (11) : l'utilisateur a cliqué sur le gadget de fermeture de la fenêtre active.

SIZEWINDOW (12) : l'utilisateur en a changé la taille.

REFRESHWINDOW (13) : demande de rafraîchissement pour la fenêtre dont la structure Window est pointée par le champ ie_EventAddress.

NEWPREFS (14) : de nouvelles préférences ont été sélectionnées. Non fonctionnel sous 1.3, obsolète depuis.

DISKREMOVED (15) et DISKINSERTED (16) : un disque a été retiré ou inséré dans une unité. Le champ ie_Code est censé indiquer l'unité en question mais il ne faut pas trop s'y fier car cela peut concerner autre chose que les quatre lecteurs de disquette (CD, SyQuest...).

ACTIVEWINDOW (17) et INACTIVEWINDOW (18) : une fenêtre est activée ou inactivée. L'adresse de la structure Window est dans ie_EventAddress.

NEWPOINTERPOS (19) : différentes opérations concernant le pointeur de souris. L'octet ie_SubClass en indique le type. IESUBCLASS_COMPATIBLE indique un fonctionnement identique à celui de la classe POINTERPOS. IESUBCLASS_PIXEL permet de positionner le pointeur à un endroit particulier d'un écran, ie_EventAddress est un pointeur sur une structure IEPointerPixel, vous en avez eu un exemple dans l'article du mois dernier.

Les sous-classes IESUBCLASS_(TABLET|NEWTABLET) sont dédiées à la gestion des tablettes graphiques. Manquant d'informations à leur sujet, je ne m'y attarderai pas. Kickstart 2.0 (3.0 pour NEWTABLET) et plus.

MENUHELP (20) : est identique à MENULIST dans son format et indique que l'utilisateur a appuyé sur Help alors que le pointeur était sur un Menu. Kickstart 2.0+.

CHANGEWINDOW (21) : indique que l'état d'une fenêtre a été modifié : elle a pu être déplacée, retaillée, zoomée, mise en avant ou arrière... Kickstart 2.0+.

Exemple

Comme promis voici un exemple d'InputHandler fonctionnel. Ce n'est qu'un exemple ; il est réduit à sa plus simple expression et n'est pas réellement utile : on pourrait remplacer ce programme par un objet Exec dans ToolManager avec pour touche d'appel "diskinserted". Le rôle de ce programme est d'attendre l'insertion d'un disque dans une unité quelconque et d'exécuter une commande quand cela arrive. Le programme attend donc en paramètre une commande à exécuter (par exemple : Info) et installe un InputHandler à la priorité 51 (juste devant celui d'Intuition à la priorité 50). Celui-ci cherche dans la liste d'InputEvents reçus un événement de classe IECLASS_DISKINSERTED et envoie un signal à la tâche principale s'il en trouve un. Les InputEvents ne sont pas modifiés. A la réception de ce signal, la tâche exécute la commande donnée en paramètre grâce à la fonction SystemTagList() du DOS. Le programme quitte à la réception d'un Ctrl-C.

Idée

Comme un rapide essai avec ToolManager pourra vous en convaincre, (essayez d'affecter une touche d'appel "lshift diskinserted" à un objet Exec), le champ ie_Qualifiers est laissé vierge pour les événements DISKINSERTED et DISKREMOVED. On peut très facilement remédier à ce petit manque à l'aide d'un InputHandler bien proportionné... Je laisse cela à votre sagacité en vous donnant un indice : PeekQualifier(). Bonne programmation à tous !

    include exec/exec.i
    include exec/exec_lib.i
    include exec/execbase.i
    include devices/input.i
    include devices/inputevent.i
    include dos/dos.i
    include dos/dos_lib.i
    include dos/dostags.i

PRIORITE    EQU 51  ; priorité de l'InputHandler

        move.l  4.w,a6
        move.l  a6,Exec.Base
        move.l  ThisTask(a6),Handler.Interrupt+IS_DATA
        moveq   #20,d7
        cmp.w   #37,LIB_VERSION(a6) ; Kickstart 2.04 please
        blo     exit
        lea     DOS.Name(pc),a1     ; ouvre le DOS
        moveq   #37,d0
        jsr     _LVOOpenLibrary(a6)
        move.l  d0,DOS.Base
        beq     exit
        move.l  d0,a6
        move.l  #Args.Template,d1   ; lit les arguments
        move.l  #Args.Array,d2
        moveq   #0,d3
        jsr     _LVOReadArgs(a6)
        move.l  d0,Args.RDArgs
        beq     CloseDOS
        move.l  Exec.Base(pc),a6
        jsr     _LVOCreateMsgPort(a6)   ; crée un port de réponse
        move.l  d0,Input.Port
        move.l  d0,a0
        moveq   #IOSTD_SIZE,d0
        jsr     _LVOCreateIORequest(a6)
        move.l  d0,Input.IO ; crée une struct IOStdRequest
        beq.s   FreeIO
        move.l  d0,a1
        lea     Input.Name(pc),a0   ; ouvre l'input.device
        moveq   #0,d0
        move.l  d0,d1
        jsr     _LVOOpenDevice(a6)
        tst.l   d0
        bne.s   FreeIO
        moveq   #-1,d0
        jsr     _LVOAllocSignal(a6) ; alloue un bit de signal
        move.b  d0,DiskInserted.SigBit
        bmi.s   CloseInput
        moveq   #0,d7
        move.l  Input.IO(pc),a1
        move.w  #IND_ADDHANDLER,IO_COMMAND(a1)  ; on ajoute un handler
        move.l  #Handler.Interrupt,IO_DATA(a1)
        jsr     _LVODoIO(a6)    ; pas de code de retour

MainLoop
        move.l  #SIGBREAKF_CTRL_C,d0    ; attend un signal
        move.b  DiskInserted.SigBit(pc),d1
        bset    d1,d0
        jsr     _LVOWait(a6)
        bclr    #SIGBREAKB_CTRL_C,d0    ; ctrl-C => on sort
        bne.s   Quit
; on exécute la commande car un disque a été inséré
        bsr     ExeCommand
        bra.s   MainLoop

Quit    move.l  Input.IO(pc),a1
        move.w  #IND_REMHANDLER,IO_COMMAND(a1)  ; on retire le handler
        move.l  #Handler.Interrupt,IO_DATA(a1)
        jsr     _LVODoIO(a6)
        move.l  DiskInserted.SigBit(pc),d0
        jsr     _LVOFreeSignal(a6)  ; on libère le signal
CloseInput
        move.l  Input.IO(pc),a1 ; on ferme l'input.device
        jsr     _LVOCloseDevice(a6)
FreeIO  move.l  Input.IO(pc),a0 ; on libère la struct IORequest
        jsr     _LVODeleteIORequest(a6) ; (accepte un argument nul)
        move.l  Input.Port(pc),a0
        jsr     _LVODeleteMsgPort(a6)   ; on libère le port (idem)
        move.l  DOS.Base(pc),a6
        move.l  Args.RDArgs(pc),d1  ; et les arguments
        jsr     _LVOFreeArgs(a6)
CloseDOS
        move.l  DOS.Base(pc),a1 ; on ferme le DOS
        move.l  Exec.Base(pc),a6
        jsr     _LVOCloseLibrary(a6)
exit    move.l  d7,d0   ; code de retour
        rts

; code du handler: voir l'article
Handler.Code    ; (D0)NewEvents=Handler.Code(Events,Data)(A0,A1)
        movem.l d1/a0-a1/a6,-(sp)
        move.l  a0,d0
        beq.s   .No
.Loop   move.l  d0,a0   ; InputEvent suivant
        cmp.b   #IECLASS_DISKINSERTED,ie_Class(a0)
        beq.s   .Yes    ; est-ce la bonne classe?
        move.l  ie_NextEvent(a0),d0 ; non: on passe au suivant
        bne.s   .Loop   ; fini?
        bra.s   .No

.Yes    moveq   #0,d0   ; envoie un signal à la tâche
        move.l  d0,d1
        move.b  DiskInserted.SigBit(pc),d1
        bset    d1,d0
        move.l  Exec.Base(pc),a6
        jsr     _LVOSignal(a6)  ; A1=Structure Task (champ is_Data)
.No     movem.l (sp)+,d1/a0-a1/a6
        move.l  a0,d0
        rts

; cette routine exécute la commande spécifiée sur la ligne d'appel du
; programme grâce à la fonction SystemTagList() du DOS.
ExeCommand
        movem.l d0-d1/a0-a1/a6,-(sp)
        move.l  Args.Array(pc),d1   ; nom et arguments
        move.l  #Command.Tags,d2    ; 0 en fait
        move.l  DOS.Base(pc),a6
        jsr     _LVOSystemTagList(a6)
        movem.l (sp)+,d0-d1/a0-a1/a6
        rts

Exec.Base           dc.l    0
DOS.Base            dc.l    0
Args.Array          dc.l    0
Args.RDArgs         dc.l    0
Input.Port          dc.l    0
Input.IO            dc.l    0

Handler.Interrupt   dc.l    0,0
                    dc.b    NT_INTERRUPT,PRIORITE
                    dc.l    DiskWatcher.Name    ; pas vraiment utile
                    dc.l    0   ; adresse de la tâche
                    dc.l    Handler.Code

Command.Tags        EQU 0   ; aucun tag

DOS.Name            dc.b    'dos.library',0
Args.Template       dc.b    'COMMAND/A/F',0
DiskInserted.SigBit dc.b    -1
DiskWatcher.Name    dc.b    'DiskWatcher',0
Input.Name          dc.b    'input.device',0



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