Suivez-nous sur X

|
|
|
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
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Programmation graphique système - 4e partie : ouvrir un écran Intuition
(Article écrit par l'équipe de GuruMed et extrait de GuruMed.net - août 2002)
|
|
Aujourd'hui, nous allons voir comment ouvrir notre fenêtre sur un écran privé plutôt que sur le Workbench.
Deux intérêts à cela : avoir accès à toutes les couleurs de la palette, et bien sûr faire du plein écran
à la résolution souhaitée au lieu d'avoir une petite fenêtre perdue au milieu du Workbench. De plus, cela
permet de faire un meilleur multi tampon mémoire (double ou triple tampon, pour fluidifier les animations),
mais ce sujet sera abordé dans un autre article.
La partie "compliquée" consiste à ouvrir un écran qui soit visible sur la configuration de l'utilisateur...
A priori, sur AGA, pas de problème. Cependant, quand on a une carte graphique, le système a une fâcheuse
tendance à ouvrir un écran PAL, ou d'une taille n'ayant rien à voir avec la résolution demandée, ce qui
est assez peu pratique.
Une des solutions à ce problème est de demander à l'utilisateur sur quel écran il veut que le programme
s'ouvre, à l'aide d'une requête ASL. De cette façon, si l'écran ne convient pas, ça sera la faute de
l'utilisateur. Nah.
Utiliser une requête ASL
Nous allons utiliser l'asl.library v38 ou supérieure (la requête de mode d'écran n'existe que depuis cette
version). Comme expliqué précédemment, je n'ouvrirai cependant pas explicitement cette bibliothèque
dans le programme d'exemple afin d'aller droit au but ; mais si vous voulez programmer proprement pour
que votre programme ne plante pas sur une version inférieure du système d'exploitation, il vous faudra
tester d'une façon ou d'une autre que la version est la bonne.
Il faut d'abord utiliser la fonction AllocAslRequest() pour préciser ce qu'on veut comme requête :
struct ScreenModeRequester *req;
req = (struct ScreenModeRequester *) AllocAslRequestTags( ASL_ScreenModeRequest,
ASLSM_TitleText, (ULONG)"Choisissez une résolution",
TAG_END );
|
- ASL_ScreenModeRequest : ce premier paramètre signifie que l'on veut ouvrir une requête de mode d'écran.
- ASLSM_TitleText : cette balise permet de choisir le titre de la requête.
Il existe bien sûr d'autres balises, parmi lesquelles :
- ASLSM_InitialLeftEdge, ASLSM_InitialTopEdge, ASLSM_InitialWidth, ASLSM_InitialHeight : ces balises
permettent de choisir la position et la taille de la fenêtre de requête.
- ASLSM_InitialDisplayID : celle-ci permet de choisir le ModeID qui sera présélectionné dans la liste.
- ASLSM_Window : pour associer la requête à une fenêtre particulière (par exemple, une fenêtre
de préférences contenant un bouton "Choisir la résolution" qui ouvre la requête quand on clique dessus).
- ASLSM_SleepWindow (booléen) : afin de bloquer tous les évènements à destination de la fenêtre
associée à la requête tant que celle-ci est ouverte.
Filtrer la liste des modes d'écran...
Il existe également toute une série de balises destinées à filtrer les modes d'écran qui feront partie
de la liste affichée dans la requête : ASLSM_MinDepth, ASLSM_MaxDepth, ASLSM_MinWidth, ASLSM_MinHeight,
ASLSM_MaxWidth et ASLSM_MaxHeight.
Par exemple, vous pourriez limiter la liste aux modes d'une largeur supérieure ou égale à 640 pixels
en rajoutant "ASLSM_MinWidth, 640" dans la liste des balises.
Malheureusement, ça ne marche pas. En tout cas pas sous CyberGraphX. La seule solution sûre
est d'utiliser un "hook" ("crochet" en français, fonction permettant le contrôle (monitoring) des messages d'entrée-sortie d'une application).
- ASLSM_FilterFunc : cette balise prend en paramètre l'adresse d'un hook, qui est une structure
contenant l'adresse d'une fonction à appeler pour chaque mode d'écran présent dans le système. Si cette
fonction renvoie TRUE, alors le mode d'écran sera présent dans la liste. Sinon, il est ignoré.
Le hook est un peu plus difficile à utiliser que les balises Min/Max citées ci-dessus, mais heureusement,
il permet un contrôle encore plus grand sur les modes d'écran affichés dans la requête.
Voici un exemple de hook qui filtre tous les modes d'écran inférieurs à 640x400 et d'une profondeur
maximum différente de 8 bits :
BOOL scrModeFilter( struct Hook *hook, struct ScreenModeRequester *smr, ULONG modeID )
{
struct DimensionInfo di;
if( GetDisplayInfoData( NULL, (UBYTE*)&di, sizeof(struct DimensionInfo), DTAG_DIMS, modeID )
>= (BYTE*)&di.Nominal.MaxY + 2 - (BYTE*)&di ) {
if( di.Nominal.MaxX+1 < 640 || di.Nominal.MaxY+1 < 400 || di.MaxDepth != 8 ) return FALSE;
}
return TRUE;
}
|
Ce hook utilise la fonction GetDisplayInfoData() de la graphics.library pour récupérer des informations
sur le mode d'écran. Cette fonction permet de remplir quatre structures de données différentes ;
nous avons ici besoin des informations qui sont dans la structure DimensionInfo.
Une petite explication au sujet du test sur la valeur de retour de GetDisplayInfoData() : la fonction
retourne le nombre d'octets écrits dans la structure. Parmi les trois champs que l'on utilise ici,
Nominal.MaxY étant le plus éloigné du début de la structure, on vérifie que la fonction a copié
suffisamment d'octets pour que ce champ (et donc également les autres qui nous intéressent) ait
été rempli. Je ne peux que vous conseiller de consulter l'AutoDoc de GetDisplayInfoData()ainsi que
le fichier d'inclusion graphics/displayinfo.h pour en apprendre plus.
Information obsolète mais intéressante : notez également l'emploi des macros SAVEDS et REG(), qui sont
définies dans le fichier SDI_Compiler.h se trouvant dans l'archive CLib-SDI.lha
sur Aminet. L'utilisation de ces macros devrait vous permettre de rendre votre code source compatible avec
n'importe quel compilateur.
Le hook s'utilise de cette façon :
struct ScreenModeRequester *req;
struct Hook hook = { NULL, NULL, (HOOKFUNC)HookEntry, (HOOKFUNC)scrModeFilter, 0 };
req = (struct ScreenModeRequester *) AllocAslRequestTags( ASL_ScreenModeRequest,
ASLSM_FilterFunc, (ULONG)&hook,
TAG_END );
|
Dans cette nouvelle version, nous utilisons la fonction HookEntry() de l'amiga.lib qui se charge elle-même
de placer les paramètres dans les bons registres. L'intérêt est multiple : d'une part, le source sera forcément
compilable avec tous les compilateurs sans utiliser de macros additionnelles, et d'autre part le hook
pourra être compilé pour PowerPC (ou autre). Cependant, l'amiga.lib de vbcc pour WarpOS et MorphOS ne
contient pas cette fonction à l'heure où j'écris ces lignes...
Ouvrir la requête de mode d'écran
Une fois la requête préparée avec AllocAslRequest(), on peut l'ouvrir :
LONG modeID = INVALID_ID;
if( AslRequest( req, NULL ) ) {
modeID = req->sm_DisplayID;
}
FreeAslRequest( req );
|
La variable "modeID" contient après ceci le ModeID à utiliser lors de l'ouverture de l'écran, ou
INVALID_ID si la requête a échoué.
Ouvrir un écran Intuition
On peut maintenant ouvrir l'écran, avec la fonction OpenScreenTagList() (ou OpenScreenTags()) de
l'intuition.library :
struct Screen *scr;
UWORD depth = DEPTH;
scr = OpenScreenTags( NULL,
SA_Title, (ULONG) "Super titre",
SA_Type, CUSTOMSCREEN,
SA_DisplayID, modeID,
SA_Depth, depth,
TAG_END );
|
- NULL : tout comme pour OpenWindowTags(), le premier paramètre est un pointeur vers une
structure dont on peut très bien se passer (une structure NewScreen). Nous nous en passons donc.
- SA_Title : cette balise sert évidemment à choisir le titre de l'écran. Elle est demandée
dans l'AutoDoc d'OpenScreenTagList() de toujours fournir un titre lorsqu'on ouvre un écran.
- SA_Type : l'écran peut être privé (CUSTOMSCREEN) ou public (PUBLICSCREEN). S'il est public,
alors d'autres programmes auront le droit d'ouvrir leurs fenêtres dessus.
- SA_DisplayID : voici la balise qui nous concerne plus particulièrement aujourd'hui. Elle
permet de donner le mode d'écran que l'on désire utiliser. On passe donc ici en paramètre la variable
modeID retournée par la requête ASL.
- SA_Depth : cette balise sert à préciser la profondeur de l'écran. Elle est très importante
de la spécifier, car la profondeur par défaut sous AGA et Picasso96 est d'un plan de bits (2 couleurs),
tandis que CyberGraphX choisit lui 8 plans de bits (256 couleurs)...
Précision importante : sur carte graphique, il vaut mieux choisir une profondeur de 8 bits même si on
utilise moins de 256 couleurs, car c'est souvent beaucoup plus rapide. Cela dépend du système utilisé,
de certains réglages, ainsi que des fonctions de tracé utilisées, mais en tout cas ce n'est jamais plus
lent que sur un écran de profondeur plus réduite. Par contre, en AGA, il faut réduire au maximum le
nombre de plans pour aller plus vite.
Nous devons donc déterminer si le ModeID est celui d'un mode natif ou d'un mode sur carte graphique. Si
c'est une carte graphique, on utilisera une profondeur de 8 plans de bits.
Pour cela, nous allons utiliser cette fois encore la fonction GetDisplayInfoData() :
UWORD depth = DEPTH;
struct DisplayInfo di;
if( GetDisplayInfoData( NULL, (UBYTE*)&di, sizeof(struct DisplayInfo), DTAG_DISP, modeID )
>= (BYTE*)&di.PropertyFlags + 4 - (BYTE*)&di ) {
if( di.PropertyFlags & DIPF_IS_FOREIGN ) depth = 8;
}
|
"DEPTH" est ici censé être une constante correspondant à la profondeur minimum nécessaire pour votre écran.
Ouvrir une fenêtre sur l'écran...
Une fois l'écran ouvert, il ne reste plus qu'à ouvrir notre fenêtre dessus. Il n'est pas forcément
indispensable d'ouvrir une fenêtre puisqu'on peut tracer directement dans le bitmap de l'écran, celui-ci
étant privé... Mais c'est souvent utile, notamment pour obtenir des évènements clavier ou souris de
la part d'Intuition.
struct Window *win;
win = OpenWindowTags( NULL,
WA_CustomScreen, (ULONG) scr,
WA_InnerWidth, WIN_WIDTH,
WA_InnerHeight, WIN_HEIGHT,
WA_Activate, TRUE,
TAG_END );
|
- WA_CustomScreen : voici la balise à utiliser pour que la fenêtre ne s'ouvre pas sur le
Workbench mais sur un écran privé. En paramètre, l'adresse de l'écran qu'on vient d'ouvrir...
- WA_Activate (booléen) : rend la fenêtre active dès son ouverture.
Si vous souhaitez faire du plein écran en cachant les bordures de la fenêtre, deux autres balises
vous seront utiles :
- WA_Borderless (booléen) : pour tracer une fenêtre sans bordures.
- WA_Backdrop (booléen) : pour mettre cette fenêtre en arrière-plan...
Le source
Afin de vérifier que tout cela fonctionne comme prévu, voici le source d'exemple du jour, qui reprend
plus ou moins tous les bouts de code de l'article mis bout à bout. Ceci permet également de voir la
fonction CloseScreen(), qui sert comme son nom l'indique à fermer un écran Intuition.
/*
** Test de requête de mode d'écran ASL.
** Ouvre une fenêtre sur un écran et attend que le gadget de fermeture soit pressé.
*/
#include <clib/alib_protos.h>
#include <graphics/displayinfo.h>
#include <libraries/asl.h>
#include <proto/asl.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#define WIN_WIDTH 640
#define WIN_HEIGHT 400
#define DEPTH 4
BOOL scrModeFilter( struct Hook *hook, struct ScreenModeRequester *smr, ULONG modeID )
{
struct DimensionInfo di;
if( GetDisplayInfoData( NULL, (UBYTE*)&di, sizeof(struct DimensionInfo), DTAG_DIMS, modeID )
>= (BYTE*)&di.Nominal.MaxY + 2 - (BYTE*)&di ) {
if( di.Nominal.MaxX+1 < WIN_WIDTH || di.Nominal.MaxY+1 < WIN_HEIGHT || di.MaxDepth < DEPTH )
return FALSE;
}
return TRUE;
}
int main( void )
{
struct ScreenModeRequester *req;
struct Hook hook = { NULL, NULL, (HOOKFUNC)HookEntry, (HOOKFUNC)scrModeFilter, 0 };
LONG modeID = INVALID_ID;
struct Screen *scr;
UWORD depth = DEPTH;
struct DisplayInfo di;
struct Window *win;
req = (struct ScreenModeRequester *) AllocAslRequestTags( ASL_ScreenModeRequest,
ASLSM_FilterFunc, (ULONG)&hook,
ASLSM_TitleText, (ULONG)"Choisissez une résolution",
TAG_END );
if( AslRequest( req, NULL ) ) {
modeID = req->sm_DisplayID;
}
FreeAslRequest( req );
if( GetDisplayInfoData( NULL, (UBYTE*)&di, sizeof(struct DisplayInfo), DTAG_DISP, modeID )
>= (BYTE*)&di.PropertyFlags + 4 - (BYTE*)&di ) {
if( di.PropertyFlags & DIPF_IS_FOREIGN ) depth = 8;
}
scr = OpenScreenTags( NULL,
SA_Title, (ULONG) "Super titre",
SA_Type, CUSTOMSCREEN,
SA_DisplayID, modeID,
SA_Depth, depth,
TAG_END );
win = OpenWindowTags( NULL,
WA_CustomScreen, (ULONG) scr,
WA_Width, WIN_WIDTH,
WA_Height, WIN_HEIGHT,
WA_Activate, TRUE,
WA_CloseGadget, TRUE,
WA_IDCMP, IDCMP_CLOSEWINDOW,
TAG_END );
if( win ) {
WaitPort( win->UserPort );
CloseWindow( win );
}
if( scr ) CloseScreen( scr );
}
|
Peut-être avez-vous l'impression que j'ai oublié de nombreux tests de sécurité (allocation de la requête
ASL, ouverture de l'écran...) ; mais en fait, ce programme devrait se comporter parfaitement en cas
de problème : AslRequest() échouera si "req" vaut "NULL", "modeID" vaudra alors "INVALID_ID"
et l'ouverture de l'écran échouera à cause de ça, et enfin l'ouverture de la fenêtre échouera si elle
reçoit "NULL" comme paramètre à WA_CustomScreen. Tout est sous contrôle. ;-)
Ah, un petit détail pour ceux qui ne le savent pas : avec vbcc, il suffit de rajouter "-+" ou "-c99" sur
la ligne de commande pour que les commentaires "//" soient acceptés. Je vous rappelle également
qu'il faut rajouter "-lauto" pour que les bibliothèques soient ouvertes automatiquement.
Merci à David "Zapek" Gerber et Stéphane "Sara" Saragaglia pour leur aide sur les hooks et à
Mathias "Corto" Parnaudeau pour les tests en AGA.
|