Obligement - L'Amiga au maximum

Lundi 02 juin 2025 - 04:01  

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 : 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.


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