|
||||||||||||||||||||||||||||||||||||||||||||
|
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 !
|