Obligement - L'Amiga au maximum

Vendredi 19 avril 2024 - 21:03  

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 - Création et gestion d'une fenêtre de requête
(Article écrit par Max et extrait d'Amiga News Tech - octobre 1989)


Voici le listing d'un programme qui devrait vous montrer comment créer et gérer une véritable requête sous Intuition.

Ce programme met en fait en pratique toute la théorie que nous avons vue jusqu'ici : écran, fenêtres, menus et bien sûr requêtes. D'une taille conséquente (plus de 550 lignes de code source !), il a été écrit avec l'assembleur Devpac Amiga version 2, mais son adaptation à tout autre assembleur ne devrait poser aucun problème.

J'ai évité l'emploi de fichiers "include", ce qui permettra aux possesseurs du K-Seka d'en profiter également (et du même coup, accélère les temps d'assemblage...). Cela dit, rien ne vous empêche, si vous avez la flemme de taper tout les EQUates préliminaires, d'inclure les fichiers exec.i, exec lib.i, intuition.i, intuition lib.i, graphics.i, graphics lib.i et diskfont lib.i.

; Ce programme est une démonstration	
; de la manière de construire et de	
; gérer un Requester.	
; Assemblé tel quel, il n'occupe	
; que 1884 octets pour quelque chose (2272 après corrections)
; comme 550 lignes de code source)!	=> 609 !!
; Définition des bits de flags pour	
; l'écran, la fenêtre et les gadgets
; Converti d'images vers texte, debogué, corrigé et testé par Kamel Biskri 29/11/2020	

MODE_640	    EQU	$8000
CUSTOMSCREEN	    EQU	$0F
GADGETDOWN  	    EQU	$20
GADGETUP	    EQU	$40
MENUPICK	    EQU	$100
WINDOWDRAG	    EQU	$2
BACKDROP	    EQU	$100
BORDERLESS	    EQU	$800
ACTIVATE	    EQU	$1000
RMBTRAP	            EQU	$10000

ITEMTEXT            EQU	2
COMMSEQ	            EQU	4
ITEMENABLED	    EQU	$10
HIGHCOMP	    EQU	$40
GADGHCOMP	    EQU	0
GADGHNONE	    EQU	3
RELVERIFY	    EQU	1
BOOLGADGET	    EQU	1
PROPGADGET	    EQU 3
STRGADGET	    EQU	4
AUTOKNOB	    EQU	1
FREEHORIZ	    EQU	2

;Défintion des fonctions et des bibliothèques
;Exec Library
ExecBase                = 4
OpenLibrary	    EQU	-552
CloseLibrary	    EQU	-414
GetMsg	            EQU	-372
ReplyMsg	    EQU	-378

;Intuition Library
OpenScreen	    EQU	-198
CloseScreen	    EQU	-66
OpenWindow	    EQU	-204
CloseWindow	    EQU	-72
SetMenuStrip        EQU	-264
ClearMenustrip      EQU	-54
AutoRequest	    EQU	-348
DisplayBeep	    EQU	-96

;Graphics Library
OpenFont	    EQU	-72
CloseFont	    EQU	-78

;DiskFont Library
OpenDiskFont        EQU	-30

;Autres definitions utiles
UserPort	    EQU	86
MsgClass	    EQU	20
MsgCode	            EQU	24
MsgAddress	    EQU	28

*******************
*Debut du programe*
*******************
Start:
    movem.l	d1-d7/a0-a6,-(sp)
    bsr	OpenInt     			; Ouvre Intuition
    bne.s okInt     			; Tout va bien ?
    move.l #20,d0   			; Non : on s'barre
    bra	Fin
okInt:
    bsr     OpenGfx
    bne.s   OkGfx
    bsr      CloseInt
    move.l  #30,d0
    bra	    Fin

OkGfx:
    bsr	    OpenS
    bne.s   OkScr
    bsr	    CloseGfx
    bsr	    CloseInt
    move.l  #40,d0
    bra	    Fin

OkScr:
    lea	    WindowDefs,a0
    bsr	    OpenW
    bne.s   OkWin
    bsr	    CloseS
    bsr	    CloseGfx
    bsr	    CloseInt
    move.l  #50,d0
    bra	    Fin

OkWin:	
    move.l	IntBase,a6          ; Coucou le menu
    move.l	WinHandle,a0
    lea	    MenuDefs,a1
    jsr	    SetMenuStrip(a6)
    
    ;Saut à la routine principale
    bsr	    Main
    
    move.l  IntBase,a6
    move.l  WinHandle,a0
    jsr     ClearMenustrip(a6)

    move.l  WinHandle,a0
    bsr	    CloseW              ; Ferme la fenêtre
    bsr	    CloseS              ; Ferme l'écran
    bsr	    CloseGfx            ; Ferme Graphics
    bsr     CloseInt	        ; Et Intuition
    clr.l   d0                  ; R. A. S
Fin:
    movem.l (sp)+,d1-d7/a0-a6
    rts                         ; Et ciao la compagnie

;**********************
;* Routine principale *
;* (sortie par RTS)   *
;**********************
Main:

MainLoop:
    ;Message pour moi ?
    move.l  ExecBase,a6
    move.l  WinHandle,a0
    move.l  UserPort(a0),a0
    Jsr	    GetMsg(a6)
    move.l  d0,a0
    tst.l   d0
    beq.s   MainLoop        	; Non
    move.l  MsgClass(a0),d0 	; Oui
    cmp.l   #MENUPICK,d0    	; C'est le nenu ?
    bne.s   MainLoop        	; Non

DoMenu:
    move.w  MsgCode(a0),d0  	; Oui
 ; Ici, on calcule quel item
 ; de quel menu ou de quel
 ; sous-menu a été selectionné
 
    move.w d0,d2
    lsr.w   #8,d2
    lsr.w   #3,d2           	; N° de sous-menu dans d2
    ; *** lsr.w	#11,d2 	 	; est impossible

    move.w  d0,d1
    lsr.w   #5,d1
    and.w   #$3f,d1         	; N° d'item dans d1

    and.w   #$1f,d0         	; N° de menu dans d0

 ; ********************
 ; * Reaction au menu *
 ; ********************
DoMenu1:
    tst.w   d0
    bne.s   DoMenu2
DoMenu1_1:
    tst.w   d1
    bne.s   DoMenu1_2
    rts     mem                	; Sortie de "Main"
DoMenu1_2:
    cmp.w   #1,d1
    bne.s   Do_Menu1_3
    bsr.s   DoRequest

Do_Menu1_3:
 ;***Rien à faire ici (et pour cause !)
DoMenu2:
 ;*** Ici non plus
    bra	    MainLoop

 ;************************
 ;* Gestion du requester *
 ;************************
DoRequest:
    lea	    ReqWindowDefs,a0
    bsr	    OpenW             	; Ouvre la fenêtre
    bne.s   DoRequestLoop     	; du Requester
 ; Mais retourne a  "Main"
 ; en cas d'echec !
    rts
DoRequestLoop:
    move.l  ExecBase,a6
    move.l  ReqWinHandle,a0
    move.l  UserPort(a0),a0
    jsr	    GetMsg(a6)      	; Message Requester ?
    tst.l   d0
    beq	    DoRequestLoop   	; Non
    move.l  d0,a1           ; Ah s1, y'en a un!
    move.l  MsgClass(a1),d2
    move.l  MsgCode(a1),d3
    move.l  MsgAddress(a1),a2

 ; Il convient, bien que
 ; ce ne soit pas obligatoire,
 ; de répondre au message,
 ; histoire de dire à Madame
 ; Intuition qu'on l'a bien reçu.
 ; ReplyMsg() fait cela très bien.

    Jsr	ReplyMsg(a6)

    move.w	$26(a2),d0      	; gg_Gadget ID dans d0
    sub.w	#61,d0          	; Etait-ce "OK" ?
    beq.s	DoReqGad1
    subq.w	#1,d0           	; Ou "Ca clignote !" ?
    beq.s	DoReqGad2
    subq.w	#1,d0           	; Ou le String-Gadget ?
    beq.s	DoReqGad3
	
 ; Aucun des 3, donc on boucle	
    bra	DoRequestLoop

 ; Traitement du gadget "OK"
DoReqGad1:
    move.l	ReqWinHandle,a0
    bsr	CloseW              	; Ferme le Requester
    rts	                    	; Et retourne à "Main"

 ; Traitement du gadget "Ca clignote !" ?

DoReqGad2:
    move.l	IntBase,a6
    move.l	ScrHandle,a0
    jsr	DisplayBeep(a6)     	; L'écran clignote
    bra	DoRequestLoop
 ; Traitement du String-Gadget
 ; On va sinplement afficher
 ; la chaîne de caractères entrée
 ; dans le String-Gadget dans un
 ; autre Requester, mais de type
 ; plus simple celui-là, puisqu'il
 ; s'agit d'un bête AutoRequester.

DoReqGad3:
    move.l  IntBase,a6
    move.l  ReqWinHandle,a0
    lea	    BodyiText,a1
    lea	    ButtoniText,a2
    lea	    ButtoniText,a3
    move.l  #0,d0
    move.l  #0,d1
    move.l  #300,d2
    move.l  #60,d3
    jsr	    AutoRequest(a6)
    bra	    DoRequestLoop
    
 ; Éventuellement, si d'autres
 ; gadgets sont à traiter,
 ; c'est ici que cela se passe...

DoReqGad4:
    bra     DoRequestLoop

 ;**********************************
 ;* Sous routines d'initialisation *
 ;**********************************
OpenInt:
    move.l  ExecBase,a6
    clr.l   d0                  
    lea	    IntName,a1
    jsr     OpenLibrary(a6)
    move.l  d0,IntBase
    rts

CloseInt:
    move.l  ExecBase,a6
    move.l  IntBase,a1
    jsr	    CloseLibrary(a6)
    rts

OpenGfx:
    move.l  ExecBase,a6
    clr.l   d0                  
    lea     GfxName,a1
    jsr     OpenLibrary(a6)
    move.l  d0,GfxBase
    rts

CloseGfx:
    move.l  ExecBase,a6
    move.l  GfxBase,a1
    jsr     CloseLibrary(a6)
    rts

 ; En plus d'ouvrir l'écran,
 ; la routine OpensS s'occupe
 ; de charger en mémoire la police de caractères
 ; "emerald 17".
 
 ; Pourquoi OpenS ?
 ; Et pourquoi pas ?

OpenS:
    move.l	ExecBase,a6
    clr.l       d0                  
    lea	DiskFontName,a1
    jsr	OpenLibrary(a6)
    move.l	d0,DiskfontBase
    beq.s	NoDiskFont

    move.l	d0,a6
    lea	        FontDefs,a0
    jsr	        OpenDiskFont(a6)
    move.l	d0,TextFont
    
NoDiskFont:
    move.l	IntBase,a6
    lea	        ScreenDefs,a0
    jsr	        OpenScreen(a6)
    move.l	d0,ScrHandle
    rts
 ; CloseS se charge également
 ; de libérer la mémoire
 ; Occupée par la police de caractères
 ; supplémentaire.
 
CloseS:
    move.l	IntBase,a6
    move.l	ScrHandle,a0
    jsr	        CloseScreen(a6)
    move.l	GfxBase,a6
    move.l	TextFont,d0
    beq	        DontCloseFont
    move.l	d0,a1
    jsr	        CloseFont(a6)
    rts
DontCloseFont:
    move.l	ExecBase,a6
    move.l	DiskfontBase,a1
    jsr	        CloseLibrary(a6)
    rts
OpenW:
    move.l	IntBase,a6
    move.l	a0,-(sp)
    lea	        4(a0),a0
    move.l	ScrHandle,30(a0)
    jsr	        OpenWindow(a6)
    move.l	(sp)+,a0
    move.l	d0,(a0)
    rts
CloseW:
    move.l	IntBase,a6
    jsr	        CloseWindow(a6)
    rts

 ;********************
 ;* Zone des données *
 ;********************

IntBase:	    dc.l    0
GfxBase:	    dc.l    0
DiskfontBase:       dc.l    0
ScrHandle:	    dc.l    0
TextFont:	    dc.l    0
IntName:	    dc.b    "intuition.library",0
    even
GfxName:	    dc.b	"graphics.library",0
    even
DiskFontName:       dc.b	"diskfont.library",0
    even

 ;*** Structure NewScreen

ScreenDefs:
    dc.w	0,0,640,256,2
    dc.b	2,1
    dc.w	MODE_640,CUSTOMSCREEN
    dc.l	0,ScrTitre,0,0
ScrTitre:
    dc.b	"Commodore Revue",0
    even
 ;*** Structure NewWindow
WindowDefs:
WinHandle:
    dc.l	0
    dc.w	0,0,640,256
    dc.b	0,1
    dc.l	MENUPICK
    dc.l	BACKDROP|BORDERLESS|ACTIVATE
    dc.l	0,0,0,0,0       ; Pas de titre !
    dc.w	50,50,320,256,CUSTOMSCREEN

 ; *** Structure NewWindow pour le Requester
ReqWindowDefs:
ReqWinHandle:
    dc.l        0
    dc.w	0,11,320,120
    dc.b	0,1
    dc.l	GADGETUP|GADGETDOWN
    dc.l	WINDOWDRAG|ACTIVATE|RMBTRAP
    dc.l	ReqGadgetDefs
    dc.l        0,ReqWinTitre,0,0   ; Pas de titre
    dc.w	0,0,0,0,CUSTOMSCREEN
ReqWinTitre:
    dc.b	"Et un Requester, un !",0
    even

 ; *** Structures Gadget, IntuiText, Border
 ; et Image pour le Requester
ReqGadgetDefs:
ReqGadget1:
    dc.l	ReqGadget2	; NextGadget
    dc.w	25,100,40,9     ; Pos & Taille
    dc.w	GADGHCOMP	; Flags
    dc.w	RELVERIFY       ; Activation Flags
    dc.w	BOOLGADGET      ; Gadget Type (boolean)
    dc.l	ReqBorder1      ; Gadget Image
    dc.l	0               ; Gadget Select
    dc.l	ReqGad1iText    ; Gadget Text
    dc.l	0               ; Mutual Exclude
    dc.l        0               ; Special Info
    dc.w	60+1	        ; Gadget ID
    dc.l	0               ; User Data
ReqBorder1:
    dc.w	0,0	        ; offset
    dc.b	1,1	        ; DPen, BPen
    dc.b	1               ; Write Mod e
    dc.b	5               ; Nb Coords
    dc.l	ReqCoords1      ; Coords Border
    dc.l	0           	; NextBorder
ReqGad1iText:
    dc.b	1,1	        ; Couleurs
    dc.b	0,0	        ; Write Mode, even
    dc.w	11,1	        ; xPos, yPos
    dc.l        0           	; Font
    dc.l	ReqGad1Txt      ; Texte
    dc.l        0
ReqCoords1:
    dc.w	-1,-1,40,-1,40,9,-1,9,-1,0
ReqGad1Txt:
    dc.b	"OK",0
    even
ReqGadget2:
    dc.l	ReqGadget3
    dc.w        95,100,200,9
    dc.w	GADGHCOMP,RELVERIFY,BOOLGADGET
    dc.l	ReqBorder2,0,ReqGad2iText,0,0
    dc.w	60+2            ; Gadget 1D
    dc.l        0
ReqBorder2:
    dc.w	0,0
    dc.b	1,1,1,5
    dc.l	ReqCoords2,0
ReqGad2iText:
    dc.b	1,1,0,0
    dc.w	46,1
    dc.l	0,ReqGad2Txt,0
ReqCoords2:	
    dc.w	-1,-1,200,-1,200,9,-1,9,-1,0
ReqGad2Txt:
    dc.b	"Ca clignote !",0
    even
ReqGadget3:
    dc.l	ReqGadget4
    dc.w	25,80,270,9
    dc.w	GADGHCOMP|RELVERIFY|STRGADGET
    dc.l	ReqBorder3,0,ReqGad3iText,0
    dc.l	ReqGad3Info
    dc.w	60+3
    dc.l        0
ReqBorder3:
    dc.w	0,0
    dc.b	1,1,1,5
    dc.l	ReqCoords3,0
ReqGad3iText:
    dc.b	1,1,0,0
    dc.w	0,-10
    dc.l	0,ReqGad3Txt,0
ReqCoords3:
    dc.w	-1,-1,270,-1,270,9,-1,9,-1,0
ReqGad3Txt:
    dc.b	"Un peu de place pour écrire :",0
    even
ReqGad3Info:
    dc.l	TextBuffer  ; Buffer
    dc.l	UndoBuffer
    dc.w	0           ; Curs pos
    dc.w        33          ; Max. cars
    dc.w	0           ; 1er car.
    dc.w	0,0,0,0,0
    dc.l        0           ; Rastport
    dc.l	0           ; LongInt
    dc.l        0	    ; KeyMap
TextBuffer:
    dcb.l       9
UndoBuffer:
    dcb.l       9

ReqGadget4:
    dc.l	ReqGadget5
    dc.w	25,40,270,20
    dc.w	4,0,PROPGADGET
    dc.l        ReqImage4,0,0,0
    dc.l	ReqGad4Info
    dc.w	60+4
    dc.l	0
ReqImage4:
    dc.w	0,0,0,0,1       ; Pos, Taille & Prof
    dc.l	ReqImgData4     ; Data imagee
    dc.b	0,0	        ; Couleurs
    dc.l	0	        ; Next Image
ReqImgData4:
    dc.l	0,0,0,0	        ; Pas de données

ReqGad4Info:
    dc.w	AUTOKNOB|FREEHORIZ
    dc.w	0
ReqGad4Pos:
    dc.w	0
    dc.w	$ffff/16,0
    dc.w	0,0,0,0,0,0
ReqGadget5:
    dc.l	0
    dc.w	25,16,270,20
    dc.w	GADGHNONE,0,BOOLGADGET
    dc.l	ReqBorder5,0,ReqGad5iText,0,0
    dc.w	60+5
    dc.l    0
ReqBorder5:
    dc.w	0,0
    dc.b	1,0,0,5
    dc.l	ReqCoords5,0
ReqGad5iText:
    dc.b	1,1,0,0
    dc.w	7,2
    dc.l	FontDefs,ReqGad5Txt,0
ReqCoords5:
    dc.w	-1,-1,270,-1,270,20,-1,20,-1,-1
ReqGad5Txt:
    dc.b	"VOICI DE L'EMERALD 17",0
    even
 
 ; *** Structure TextAttr pour la font (emerald 17)
FontDefs:
    dc.l	FontName
    dc.w	17
    dc.b	%0010,0     ; Style Gras
FontName:
    dc.b	"emerald.font",0
    even

 ; *** Structures IntuiText pour l'AutoRequester
BodyiText:
    dc.b	2,0,0,0
    dc.w	10,5
    dc.l	0,BodyTxt,BodyiText2
BodyTxt:
    dc.b	"Texte du String-Gadget :",0
    even
BodyiText2:
    dc.b	3,0,0,0
    dc.w	10,15
    dc.l	0,TextBuffer,0
ButtoniText:
    dc.b	3,0,0,0
    dc.w	5,3
    dc.l	0,ButtonTxt,0
ButtonTxt:
    dc.b	"Merci",0
    even
 ;*** Structure Menu
MenuDefs:
Menu1:
    dc.l        0
    dc.w	8,0,80,10,1
    dc.l	Menu1Titre,Menu1_1
    dc.w	0,0,0,0
Menu1Titre:
    dc.b	"Projet",0
    even
 ; *** Structure MenuItem
Menu1_1:
    dc.l	Menu1_2
    dc.w	0,0,110,12
    dc.w	ITEMTEXT|COMMSEQ|ITEMENABLED|HIGHCOMP
    dc.l	0,Menu1_1iText,0
    dc.b	"Q",0
    dc.l	0,0
Menu1_1iText:
    dc.b	2,0,0,0
    dc.w	3,2
    dc.l	0,Menu1_1Txt,0
Menu1_1Txt:
    dc.b	"Quitter",0
    even
Menu1_2:
    dc.l        0
    dc.w	0,12,110,12
    dc.w	ITEMTEXT|ITEMENABLED|HIGHCOMP
    dc.l	0,Menu1_2iText,0
    dc.b	0,0
    dc.l	0,0
Menu1_2iText:
    dc.b	2,0,0,0
    dc.w	3,2
    dc.l	0,Menu1_2Txt,0
Menu1_2Txt:
    dc.b	"Requester... ",0
    even
    END

Toute la première partie du programme consiste en la définition de constantes que nous utiliserons par la suite, constantes qui simplifient à la fois la lisibilité et la maintenance du programme. La plupart des assembleurs d'aujourd'hui offrent la possibilité d'inclure, grâce à la directive "include", des fichiers au sein d'un source, fichiers qui contiennent les définitions propres au système sur lequel on travaille (pour les ceusses qui ne l'auraient pas remarqué, nous, c'est l'Amiga). Cette méthode possède certes de nombreux avantages (par exemple, en cas de nouvelle version du système, il suffit de réassembler le programme avec les nouveaux includes), mais j'ai toujours préféré m'en passer, pour des raisons de vitesse d'assemblage. J'ai horreur d'aller boire un café pendant que l'ordinateur travaille.

Arrive alors le début du programme, avec le label "Start". On sauvegarde tous les registres, à l'exception de d0 qui nous servira à signaler une éventuelle erreur durant l'initialisation.

La suite, vous la connaissez déjà : ouverture des différentes bibliothèques utilisées, avec éventuellement sortie du programme en cas de défaillance (très improbable pour les bibliothèques en ROM, mais on ne sait jamais), puis ouverture d'un nouvel écran Intuition, ouverture d'une fenêtre dans cet écran, et enfin mise en place d'un menu. Vous aurez remarqué que toutes ces "ouvertures" se font par l'appel de routines (par exemple, bsr OpenS pour l'écran), ce qui n'est peut-être pas très économique en place mémoire, mais rend le programme modulaire.

On appelle ensuite la routine principale, baptisée "main" en hommage à mes (désastreux) début en C, avant de tout refermer (menu, fenêtre, écran et bibliothèques), de récupérer les registres sauvegardés, et de retourner au CLI. Car en effet, ce programme n'est accessible qu'à partir du CLI (ou du Shell, bien sûr, c'est pareil). Lancé depuis le Workbench (ce qui supposerait que vous lui ayiez dessiné une icône), il plante. Nous verrons le mois prochain pourquoi et comment y remédier.

La main dans la main

La routine principale "main" est en fait une boucle qui ne se terminera que lorsque l'utilisateur aura choisi l'option "Quitter" dans le menu. Elle comprend un test permanent dudit menu, grâce à la fonction GetMsg de la bibliothèque Exec. Cette fonction renvoie le cas échéant dans d0.L un pointeur sur une structure Message dans laquelle on trouvera de plus amples informations sur la teneur du message, ou bien "0" s'il ne s'est rien passé. Je ne vais pas m'amuser à décrire en détails cette structure, seulement les éléments intéressants pour nous :

Adresse Signification
Message +20 Classe du message
Message +24 Code du message
Message +28 Adresse des informations

La classe du message, appelée dans le listing MsgClass, indique quel est le type du message reçu. Étant donné que notre fenêtre n'autorise qu'un seul type de message (voir les drapeaux IDCMP dans la structure NewWindow), en l'occurrence MENUPICK, c'est le seul que nous serons habilités à recevoir. Il nous suffit donc de comparer MsgClass à la valeur MENUPICK (définie en début de listing, ça fait plaisir de voir qu'il y en a qui suivent) pour savoir si une option du menu a été sélectionnée.

Le code du message (noté MsgCode) nous aidera, lui, à déterminer quel point de menu ou de sous-menu a été sélectionné. Il s'agit d'un mot (16 bits, donc) codé comme suit :

Groupes de bits Signification
15 à 11 Sous-menu
10 à 5 Item
4 à 0 Menu

Quelques décalages à droite (à l'aide de l'instruction "lsr") suffisent donc à déterminer précisémment quel point a été choisi.

Quant à MsgAddress, il ne nous est d'aucune utilité pour le menu, alors que pour la requête, si ; nous verrons donc plus loin comment l'utiliser.

Au menu ce soir

Il nous faut maintenant réagir à la sélection du menu, ce qui est justement le but de la routine judicieusement intitulée "DoMenu". Cette routine est divisée en plusieurs sous-routines : DoMenu1, DoMenu1_1, DoMenu2, etc. Les plus perspicaces d'entre vous auront tout de suite compris que DoMenu1 s'occuppe du premier menu (celui intitulé "Projet"), que DoMenu1_1 s'occupe du premier point de ce premier menu (ici, Requester), etc.

Par contre, il peut sembler étrange qu'une routine DoMenu2 existe, alors que nous n'avons précisément pas défini de second menu. La raison est toute bête : cette manière de procéder, qui soit dit en passant n'économise pas non plus les octets, permet toutefois de modifier très aisément la structure même de la barre de menus, en ajoutant ou en enlevant des points, des sous-menus ou carrément des menu entiers. Ainsi, DoMenu2 se contente-t-elle de sauter à la boucle principale (bra MainLoop), mais si je désire ajouter un second menu, je n'aurai qu'à remplacer ce saut par une routine, identique dans l'esprit à DoMenu1.

Concrètement, comment DoMenu et ses consoeurs fonctionnent-elle ? En vérité, c'est très simple. DoMenu se charge d'abord de déterminer quel point de menu a été sélectionné. Pour ce faire, elle calcule, à l'aide de MsgCode, le numéro de menu qu'elle stocke dans le registre d0, le numéro du point de menu, stocké dans d1 et le numéro d'un éventuel sous-menu, stocké dans d2. Dans notre cas, si l'utilisateur choisit la rubrique "Quitter", d0 contiendra la valeur 0 (indiquant la premier menu), d1, la valeur 1 (indiquant le second point de menu) et d2, la valeur $3f (indiquant qu'il ne s'agit pas d'un sous-menu). Une fois ces trois valeurs initialisées, on arrive à DoMenu1.

Celle-ci se charge de déterminer, simplement en testant le registre d0, si c'est bien le premier menu qui a été choisi, sinon, elle saute à DoMenu2. Si oui, on arrive à DoMenu1_1 qui, en testant le registre d1, est capable de déterminer s'il s'agissait du premier point du premier menu (Quitter), auquel cas elle sort de la boucle principale par un simple "rts", sinon elle saute à DoMenu1_2.

De la même manière, DoMenu1_2 compare le registre d1 à pour s'assurer que c'est bien le second point du menu qui a été sélectionné, auquel cas elle appelle le sous-programme de gestion de la requête. Dans le cas contraire, elle saute à DoMenu1_3, et ainsi de suite. Rusé, non ?

Plat de résistance : la requête

Voici donc (enfin) la partie la plus intéressante de ce programme. La requête d'exemple que j'ai construite comprend deux gadgets booléens ("OK" et "Ça clignote"), un gadget de chaîne pour écrire ce qui vous passe par la tête, un gadget proportionnel horizontal, ainsi qu'un troisième gadget booléen, désactivé celui-là, contenant le texte d'illustration de la police chargée depuis le disque. Une requête supplémentaire est également mise en place par la fonction "AutoRequest()" d'Intuition lorsque l'utilisateur confirme son entrée de texte dans le string-gadget par la touche "Entrée". Tout ça est finalement très simple, mais prend beaucoup de temps à construire.

La routine qui gère tout ça s'appelle, encore une fois assez judicieusement, DoRequest. C'est une bonne habitude à prendre que de définir un modèle pour les noms des routines, cela permet, plusieurs mois plus tard, de replonger dans un programme sans se demander pendant trois heures à quoi telle partie du listing peut bien servir... Chez moi par exemple (je prends cet exemple parce que c'est celui que je connais le mieux ; rien ne vous oblige à le suivre à la lettre), tout ce qui ouvre quelque chose s'appelle OpenMachin, ce qui ferme, CloseBidule et ce qui gère un élément quelconque, DoTruc. Les anti-franglais risquent de ne pas apprécier, mais on n'est pas là pour discuter de ce genre de choses.

Je disais donc : DoRequest. La première chose à faire pour ouvrir une requête, est d'ouvrir une fenêtre dans laquelle elle sera dessinée. C'est ainsi, on n'a pas le choix. J'ai donc défini une structure NewWindow particulière baptisée ReqWindowDefs, qui contient toutes les éléments nécessaires à notre requête. Cette fenêtre pourra être déplacée à volonté, mais pas agrandie (ni même rétrécie, ça va de soi), pour des questions d'esthétique. Elle possède de plus les attributs "ACTIVATE" pour être activée dès son ouverture et "RMBTRAP" qui a pour effet de court-circuiter les effets du bouton droit de la souris. En clair, tant que la fenêtre de cette requête sera active, il sera impossible d'appeler le menu (qui appartiènt à la première fenêtre, je vous le rappelle). Concernant les drapeaux IDCMP, cette fenêtre n'autorise les messages "GADGETUP" et "GADGETDOWN", qui indiquent lorsqu'un gagdet est cliqué. Sa structure NewWindow contient par contre un pointeur essentiel sur le premier gadget de la requête.

The gadgets

Ce premier gadget est défini dans le listing au label ReqGadgetDefs, zone de mémoire divisée en plusieurs structures Gadget, IntuiText, Border et Image, nécessaires à la mise en place de la requête.

Le premier gadget, baptisé ReqGadget1, est le bouton "OK" qui met fin à la requête. Il s'agit d'un gadget booléen, c'est-à-dire pouvant prendre un état "ON" ou "OFF" uniquement. En l'occurrence, lorsqu'il sera "OFF", il faudra fermer la requête et retourner à la boucle principale MainLoop. Ce premier gadget pointe sur le second, baptisé ReqGadget2. Il s'agit encore d'un gadget booléen dans lequel figure le texte "ça clignote !". Lorsqu'il sera cliqué, la routine DoRequest se chargera de faire clignoter l'écran à l'aide de la fonction "DisplayBeep()" de la bibliothèque Intuition.

ReqGadget2 pointe sur ReqGadget3, qui n'est autre qu'un string-gadget dans lequel l'utilisateur (jusqu'à preuve du contraire, vous) pourra écrire tout ce qui lui passe par la tête, avec toutefois une limite de 33 caractères de long. Il convient ici de noter qu'Intuition n'envoie de message concernant un string-gadget que lorsque l'entrée de texte a été confirmée par la touche "Entrée". En recevant un tel message, DoRequest bâtira une seconde requête, du type SimpeRequester celui-là, qu'elle affichera à l'aide de la fonction "AutoRequest()" d'Intuition. Un SimpleRequester ne peut comporter qu'un texte et deux boutons (en général, "OK" et "Cancel"). Le fameux "Please insert volume xxxx in any drive" est un excellent exemple de SimpleRequester.

ReqGadget3 pointe, on commence à en avoir l'habitude, sur ReqGadget4, le gadget proportionnel, qu'on appelle également parfois "ascenseur". Disposé horizontalement, il n'est d'aucune utilité propre dans notre requête, sinon de montrer comment on met en place un gadget proportionnel. Quant à le gérer, Intuition ne nous facilite pas la tâche : il convient en effet de tester en permanence, donc au sein d'un boucle, la position de l'ascenseur (en lisant ses coordonnées dans sa structure de définition) et, par soustraction de ses anciennes coordonnées que l'on aura pris soin de sauvegarder quelque part, de déterminer s'il a été bougé ou non. Bref, tout le travail est à faire, ce qui peut prendre une place énorme et explique que ce ne soit point inclus dans notre programme.

Finalement, ReqGadget4 pointe sur ReqGadget5, qui terminera la liste des gadgets de la requête. Il s'agit d'un gadget de type booléen (encore), dans lequel figure un texte écrit avec la fonte Emerald 17 (figurant sur la disquette Workbench 1.3) qui a normalement été chargée lors de l'ouverture de l'écran (dans le cas contraire, aucun problème, le texte apparaîtra avec la police normale). Ce gadget comporte le drapeau "GADGHNONE", qui spécifie à Intuition qu'elle ne doit rien en faire de particulier s'il est cliqué. De fait, nous n'avons même pas à nous en préoccuper.

Changement d'adresse

Mais maintenant, comment DoRequest va-t-elle déterminer quel gadget de la requête a été cliquée ? La réponse figure dans MsgAddress, que nous avons évoqué plus haut.

Le principe est toujours le même : appel de la fonction "GetMsg()" d'Exec pour déterminer s'il est survenu un évènement quelconque à la fenêtre contenant la requête. Si rien ne se passe, on boucle sur le label DoRequestLoop. Dans le cas contraire, on commence par répondre au message, histoire qu'Exec sache qu'on l'a bien reçu et puisse le virer se sa liste des messages à nous envoyer. Ceci n'est absolument pas obligatoire, mais c'est beaucoup plus propre. De plus, on aura l'air malin devant le guru si une future version de l'AmigaOS exige absolument que l'on y réponde, à ce message. On utilise pour ce faire la fonction "ReplyMsg()" d'Exec, qui attend pour unique paramètre le pointeur sur le message reçu dans le registre a1.

C'est là qu'intervient "MsgAddress". On y trouve l'adresse d'une structure décrivant plus précisément le message envoyé. Dans le cas qui nous intéresse, on y trouvera, à l'adresse MsgAddress+$26, le numéro d'identification du gadget cliqué. Ce numéro n'est autre que celui que nous avons nous-même défini dans les différentes structures Gadget (ReqGadget1 à ReqGadget5), voyez cet article. En langage Intuition, ce numéro s'appelle GadgetID. Je me suis amusé, totalement arbitrairement, à donner des numéros d'identification compris entre 61 et 65, mais rien n'y oblige. Donnez les numéros que vous voulez, sans oublier le complémentaire.

Donc, une fois que DoRequest aura déterminé le GadgetID du gadget cliqué, elle n'aura plus qu'à appeler la routine correspondante, à savoir DoReqGad1), DoReqGad2 ou DoReqGad3 (le string-gadget), puisque ce sont les seuls dont nous nous occupons. C'est aussi simple que ça.

Définitions

La suite du listing, vous la connaissez : elle regroupe les différentes structures NewScreen, NewWindow, Gadget, etc. nécessaires à tous les éléments utilisés. On y trouve également des adresses utiles, comme IntBase ou ScrHandle, ainsi que les noms des différentes bibliothèques utilisées (intuition.lbrary, graphics.library et diskfont.library). En tout, ce sont quelques 558 lignes de code source qui, une fois assemblées, produiront un programme exécutable de 1884 octets... Impressionnant, non ?


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