Scrolling sous intuition en assembleur II utilisation de la req.library Le mois dernier nous avons vu la structure d'un écran avec les bit-planes et nous avons fait une petite routine de scrolling plutôt moyenne, pour ne pas dire franchement nul ! (du point de vue visuel, s'entend !). Au passage, j'espère que vous avez bossé les exos de la dernière fois, sinon ça va chauffer ! Ce mois-ci nous allons faire la meilleur routine de scrolle que je connaisse avec Text() (ce qui veux dire qu'il en existe peut-être d'autres et que je serais très heureux de les connaître; donc l'appel est lancé..., mais attention avec Text(), sinon c'est de la triche et justement on y reviendra dans un prochain numéro). Pour nous rendre la tâche plus facile et plus intéressante nous allons utiliser la req.library. Notre but : un File Viewer, c.a.d un programme qui affiche le contenu d'un fichier. Pour nous rendre la tâche plus dûr (NDLA : je vois déjà certains se posant des questions sur la tâche : faut-il Ariel ou Lénor ?) on devra "gérer" le caractère de contrôle 10 qui correspond au retour à la ligne (on devra donc faire des retours à la ligne). Revenons à la req.library. Cette dernière offre une fonction très intéressante : RealTimeScroll. Rien que le titre est alléchant : Scroll en temps réel. Cette fonction, couplée avec un gadget proportionnel (c'est à dire un ascenseur), doit être appelée par votre programme dès que le gadget est sélectionné. A partir de là, la routine gère le scrolling en appelant, suivant les cas, une des deux routines mises à sa disposition par vous : La première s'appelle ScrollAndDraw. Elle est chargée de scroller puis de redessiner les parties manquantes (pensez à la fonction MouseMove de la dernière fois). La seconde s'appelle RedrawAll. Elle redessine tout l'affichage (pensez à RedrawDisplay). Elle est appelée quand le déplacement de l'ascenseur est très rapide et qu'il est inutile de scroller (on gagne du temps). RealTimeScroll n'a besoin que d'un paramètre : un pointeur sur une structure ScrollStruct que voici : ULONG ss_TopEntryNumber ULONG ss_NumEntries UWORD ss_LineSpacing ULONG ss_NumLines APTR ss_PropGadget APTR ss_RedrawAll APTR ss_ReadMore APTR ss_ScrollAndDraw WORD versionnumber LONG rfu1 LONG rfu2 Bon, j'ai un petit peu menti en disant qu'elle n'avait besoin que d'un paramètre... En déquortiquant ça donne : TopEntryNumber : numéro de la première entrée affichée. Par entrée on entend la plus petite entité élémentaire indissossiable. Pour nous il s'agit d'une ligne de texte, mais il pourrait très bien s'agir d'une ligne de pixel (le mois prochain...). NumEntries : nombre total d'entrées. LineSpacing : hauteur en pixels de chaque entrée. Aujourd'hui 8. NumLines : nombre d'entrées affichables en même temps à l'écran. Aujourd'hui 30 entrées. PropGadget : pointeur sur le gadget proportionnel que doit surveiller RealTimeScroll de très près. RedrawAll : pointeur sur la routine qui sera chargée de tout redessiner. ReadMore : pointeur sur une routine qui doit être appelée quand le gadget est toujours sélectionné mais qu'il n'y a pas de déplacement. Pour nous c'est complètement inutile, donc égal à zéro. Vous me demandez à quoi ça peut servir ? Vous connaissez le FileRequester de la req.library ? Vous n'avez jamais remarqué que quand vous sélectionnez le gadget proportionnel et que tous les fichiers ne sont pas lus, la lecture continue quand même ? ScrollAndDraw : pointeur sur la fameuse routine qui se chargera de faire un super scroll. versionnumber : numéro de la version de votre req.library (en principe 1 suffit). rfu1 et rfu2 : TOP SECRET, à l'initialisation mettez les à zéro et n'y touchez plus ! C'est déjà plus clair dans votre esprit, j'espère ! Il me reste cependant à vous précisez certains petits trucs à propos de ScrollAndDraw... Et bien voilà, RealTimeScroll passe des paramètres à cette fonction par le biais des registres du 68000 (et aussi de la pile, mais c'est une autre histoire qui ne regarde que les programmeurs en C). Et oui ! Ce n'est pas fini ! Les voici : D0 : numéro de la première entrée qui doit être redessinée. D1 : endroit en pixel où doit avoir lieu le premier "dessin" (l'affichage de la première entrée). D2 : nombre de scrolls à faire avant d'envisager toute tentative d'essaie d'affichage d'entrées. (si vous vous souvenez de ScrollRaster, et bien ça correspond au paramètre qui était en D1). D3 : nombre d'entrées à dessiner. A0 : la structure ScrollStruct. Au cas où... C'est encore plus embrouillé dans votre esprit ? Tant mieux ! Ca n'en sera que plus exaltant ! On va récapitulationner un peu tout ça, histoire de digérer : Coté paramètres : ~~~~~~~~~~~~~~~~~ 1 blablablablablablablablabla <- 1 entrée 2 blablablablablablablablabla 3 blablablablablablablablabla 4 blablablablablablablablabla 5 blablablablablablablablabla |---------------------------| 6 |blablablablablablablablabla| <-\ 7 |blablablablablablablablabla| <-| 8 |blablablablablablablablabla| <-| 9 |blablablablablablablablabla| <-| 10 |blablablablablablablablabla| <-| 11 |blablablablablablablablabla| <-\ écran, c.a.d les entrées visibles |---------------------------| 12 blablablablablablablablabla 13 blablablablablablablablabla 14 blablablablablablablablabla 15 blablablablablablablablabla 16 blablablablablablablablabla 17 blablablablablablablablabla 18 blablablablablablablablabla NumEntries = 18 LineSpacing = 8 (non visible sur le schémas, mais c'est la taille d'un caractère) NumLines = 6 (6 entrées dans l'écran) Coté programme : ~~~~~~~~~~~~~~~~ Dans notre routine d'attente (qui s'appelle Attente) on dort (avec un WaitPort) jusqu'à ce qu'un événement mondiale nous réveille. Là, on regarde s'il s'agit d'un événement se rapportant à un gadget. Si ce n'est pas le cas, on ramène l'événement aux faits divers. Sinon on appelle TraiteScroll. A partir de là, TraiteScroll se charge d'initialiser une structure ScrollStruct et d'appeler RealTimeScroll. Pour l'initialisation, reportez-vous au source (NDLA : de l'événement... Oui, je sais, c'est vaseux). RealTimeScroll prend donc l'affaire en main et se charge d'appeler ScrollAndDraw ou RedrawAll suivant le cas. Une fois, que le gadget n'est plus sélectionné, on revient à TraiteScroll puis à Attente. C'est simple ! Notre routine ScrollAndDraw va être composée de deux parties : une partie qui scrolle et une partie qui affiche (si si !). je ne la détaille pas ici, vous n'avez qu'à vous reporter au source qui est suffisament explicite et au schémas ci-dessous. Un seul mot : ScrollRaster() a été remplacée par BltBitMap(). Cette dernière est utilisée pour déplacer des blocs de mémoire et aussi pour effacer une partie de l'écran après le déplacement. Pour le déplacement des blocs de mémoire nous avions le choix entre BltBitMap, BltBitMapRastPort, BltMaskBitMapRastPort et ClitBlit. Mais toutes ces fonctions ne sont que la fonction BltBitMap en plus compliqué, donc nous utilisons la plus simple qui est aussi la plus rapide. Par contre pour effacer les parties en trop il existe deux autres fonctions qui sont complétements différentes de BltBitMap, il s'agit de RectFill et BltClear. La première est déjà utilisée dans RedrawDisplay, donc on ne la réutilise pas histoire de changer. Quand à BltClear, elle est très rapide cependant elle ne permet que d'effacer des octets continus, ainsi dans notre écran, l'ascenseur disparaitrait aussi. (NDLA : si vous mettez l'ascenseur horizontalement, le problème disparait, voir plus bas). Voici un dessin qui explique comment procède ScrollAndDraw : ZORROZORROZORROZORROZORROZO ZORROZORROZORROZORROZORROZO ZORROZORROZORROZORROZORROZO |---------------------------| notre écran plein de caractères |012345678901234567890123456| |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |BLABLABLABLABLABLABLABLABLA| |---------------------------| luckylukeluckylukeluckyluke luckylukeluckylukeluckyluke luckylukeluckylukeluckyluke Quand on scrolle vers le bas (le haut apparait) : ZORROZORROZORROZORROZORROZO ZORROZORROZORROZORROZORROZO |---------------------------| |ZORROZORROZORROZORROZORROZO| |012345678901234567890123456| |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |---------------------------| BLABLABLABLABLABLABLABLABLA luckylukeluckylukeluckyluke luckylukeluckylukeluckyluke luckylukeluckylukeluckyluke En décomposant, ça donne ça : |---------------------------| Etat initial |012345678901234567890123456| |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |BLABLABLABLABLABLABLABLABLA| |---------------------------| |---------------------------| On copie une grande partie du haut |012345678901234567890123456| un peu plus bas, ici une ligne |012345678901234567890123456| plus bas |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| Pour ce faire on utilise BltBitMap |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |---------------------------| |---------------------------| Ensuite on efface la ligne du haut | | qui est en trop, toujours avec |012345678901234567890123456| BltBitMap |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |---------------------------| |---------------------------| Et on la remplace par une nouvelle |ZORROZORROZORROZORROZORROZO| ligne toute neuve, avec Text |012345678901234567890123456| C'est le scrolling |ABCDEFGHIJKLMNOPQRSTUVWXYZZ| |blablablablablablablablabla| |987654321098765432109876543| |abcdefghijklmnopqrstuvwxyzz| |---------------------------| Malheureusement, c'est un tout petit peu plus compliqué. Avec RealTimeScroll, il arrive très souvent qu'on ne se contente pas de déplacer une seule ligne à la fois. C'est pour ça qu'il passe tant de paramètres. Mais en suivant simplement ces paramètres tout ce passe très bien. (NDLA : la preuve le programme marche). Comme je l'ai dit au début, notre programme doit être capable de faire les "retour à la ligne" après le caractère de contrôle 10. Pour ce faire il faut une routine qui se charge de détecter ces caractères et c'est FindLineSize qui s'en charge. Elle renvoie l'adresse de la ligne à afficher à RedrawDisplay ou ScrollAndDraw suivant le cas. Le problème de notre "Text Viewer" c'est l'ascenseur qui prend un petit peu trop de place. A cause de ça, on ne peut afficher que 78 caractères par lignes, or certaines docs ont été conçues sur 80 lignes. Il peut donc arriver qu'une fin de ligne apparaissent sur la ligne suivante. A part ça, le programme affiche correctement une doc et peut même se révéler utile. Voici quelques options à ajouter pour le rendre encore plus convivial : - déplacement avec le clavier (ce qui implique la gestion des touches). - option permettant de cacher l'ascenseur. - gestion des codes ANSI (c'est déjà plus dûr). - menus déroulants pour options de recherche, chargement, préférences. - ascenseur horizontal (il n'y a vraiment pas grand chose à changer : remplacer 78 par 80 car on à plus de colonnes, remplacer 30 par 29 car on a moins de lignes, remplacer les "Vert" par des "Horiz", modifier les valeurs de la structure gadget et le tour est joué). Le pied serait de mettre l'ascenseur à la place de la barre de menu en offrant la possibilité de passer de l'un à l'autre par un simple clic sur le bouton droit. Une indic : il faut utiliser une fonction d'intuition pour faire apparaitre et disparaitre la barre. N'oubliez pas non plus d'enlever le gadget de l'ascenseur quand ce dernier est caché. Il y a cependant un petit problème... RealTimeScroll ne marche que pour les ascenseurs verticaux... Pour remédier à ce problème, il faut donc se passer de RealTimeScroll et de la req.library en général... Et bien c'est ce que nous ferons le mois prochain ! (Ca n'empèche qu'en général, il vaut mieux utiliser la req pour se simplifier la vie (NDLA : mais qui a dit qu'on était là pour se simplifier la vie ?...)). Tout ceci est parfaitement faisable mais demande un peu de travail. N'oubliez pas que "le premier qui m'envoie son programme amélioré, aura droit à... toute mon estime". N'oubliez pas non plus que mon but est de vous montrer comment procéder mais pas de vous faire vos programmes, c'est pour ça que j'ai mis l'ascenseur verticalement plutôt qu'horizontalement (NDLA : et aussi parce que RealTimeScroll ne marche que comme ça)). Au fait je n'ai pas expliqué la fonction FileRequester mais je pense que vous aurez compris avec les commentaires qui accompagnent la structure FR. Si vous voulez en savoir plus sur la req.library il existe des auto-docs (en anglais) sur les disquettes FISHs. Et si vous voulez que je vous en parle plus longuement, demandez-moi ! C'est tout pour aujourd'hui (il vous reste quand même pas mal de pain sur la planche). Si on est à Noël, je vous souhaite un joyeux Noël, Si on est en janvier, alors une bonne année. (NDLA : notez les rimes). Christophe LARATTE 6 allée Marcel Pagnol 87410 Le Palais sur Vienne