[C/C++] Créer une fenêtre simple avec SDL

Dans cet article, nous allons voir comment créer une fenêtre très simple avec la librairie SDL. Avec cette simple fenêtre il sera possible de faire des prototypes très basiques pour les fonctionnalités d’un projet ou pour expérimenter tout simplement. Elle peut être aussi tout simplement la base d’une future application. Je ne présente pas ici comment faire un fichier de compilation, ce n’est pas l’objectif de cet article.

Simple window with SDL

La première chose à faire pour ce programme est d’inclure la librairie dans notre code pour dire au compilateur où trouver les fonctions de SDL qu’on va utiliser. Je code cela dans le fichier includes.hpp.

1
2
3
4
5
6
7
#ifndef GAME_INCLUDES_HPP_
#define GAME_INCLUDES_HPP_

// Include STD
#include <SDL.h>

#endif // GAME_INCLUDES_HPP_

Dans ce bout de code, j’ai utilisé des macros qui vont dire au compilateur qu’on ne doit lire le code que si il n’a pas encore été rencontré. Ainsi, je pourrai appeler mon fichier includes.hpp partout sans risquer une relecture multiple, ce qui peut entraîner des erreurs de compilation. J’ai aussi commenté, si à l’avenir j’ai besoin d’ajouter d’autres librairies et que je veux améliorer la lisibilité du code.

Passons maintenant au fichier main.cpp qui va contenir le code de notre simple fenêtre. La première chose à faire est d’inclure le fichier includes.hpp dans le code.

1
#include <includes.hpp>

Ensuite, il va falloir créer la fenêtre et une surface, c’est à dire une zone de pixels pour afficher les images. Ici, pour faire simple, je vais définir des variables globales, mais le mieux serait, en C++, de les intégrer dans une classe, ou en C, dans une structure en tant qu’attributs et proposer des accès sous forme de méthodes par exemple.

1
2
SDL_Window* _sdlWindow;
SDL_Surface* _sdlSurface;

J’ai utilisé une syntaxe personnelle pour écrire les noms des variables. Je commence toujours par une minuscule pour indiquer le type de la variable et une majuscule pour son nom. Le type sdl servira pour tout les objets de SDL. Dans d’autres cas, j’utiliserai i pour int, uint pour unsigned int, etc… L’underscore est utilisé pour indiquer qu’il s’agit d’un pointeur.

Avant de parler de la fonction main, il faut écrire deux fonctions fondamentales pour bien gérer une fenêtre avec SDL. La première fonction servira à créer et initialiser la fenêtre et la surface.

1
2
3
4
int Init ( void ) {
  ...
  return 0;
}

Cette fonction peut retourner un entier signé. Lorsque tout ce passe bien dans la fonction, elle retournera 0. Par contre, si il y a une erreur, la fonction retournera -1. Avec cette information, il sera possible d’arrêter proprement le programme en cas de problème.

Dans cette fonction, il est important d’attribuer une valeur nulle à nos variables qui sont, je le rappelle, des pointeurs. On n’est jamais trop prudent avec les pointeurs qui sont sources de beaucoup d’ennuis.

1
2
  _sdlWindow = NULL;
  _sdlSurface = NULL;

C’est ici que sera créée la fenêtre. Avant tout, il faut appeler l’initialisation du sous-système vidéo indispensable à l’affichage graphique. En même temps, on vérifie qu’il n’y ait pas d’erreur à l’aide d’un entier signé similaire à celle de notre fonction. Ce n’est pas un hasard, il vaut mieux utiliser les mêmes normes que la librairie. En cas d’erreur, on va simplement envoyer un message dans le terminal (invite de commandes sous Windows) et retourner notre signal d’erreur à -1. En C++, il conviendra d’utiliser std::cerr à la place de printf() par exemple. Puis, si tout se passe bien, on crée la fenêtre avec le titre, ici un positionnement au centre de l’écran, les dimensions de l’intérieur de la fenêtre, et ici aussi si la fenêtre est visible ou non.

1
2
3
4
5
6
7
8
9
10
  if (SDL_Init (SDL_INIT_VIDEO) < 0) {
    printf ("SDL_Init, Error: %s", SDL_GetError ());
    return -1;
  }
 
  _sdlWindow = SDL_CreateWindow (
      "Simple window with SDL",
      SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
      720, 640,
      SDL_WINDOW_SHOWN);

Le dernier argument peut aussi recevoir une valeur lui indiquant si la fenêtre va permettre d’afficher du contenu OpenGL à l’aide de SDL_WINDOW_OPENGL. Les arguments ici contenant 720, 640 représentent les dimensions internes de la fenêtre, c’est à dire que les bords de la fenêtre occuperont plus de place sur votre écran. Ce sont ici les dimensions que vous voulez pour l’écran de votre application, comme-ci elle était en plein écran.

Enfin pour cette fonction, on crée la surface de rendu. On vérifie d’abord si tout c’est bien passé lors de la création de la fenêtre. Pour cela, on vérifie simplement si le pointeur n’est plus nulle. Si ce n’est pas le cas, on fait la même chose que tout à l’heure, en indiquant que l’erreur est liée à la création de la fenêtre. Puis, si tout va bien, la surface peut être créé et associé à la fenêtre.

1
2
3
4
5
6
  if (_sdlWindow == NULL) {
    printf ("SDL Create Window, Error: %s", SDL_GetError ());
    return -1;
  }
 
  _sdlSurface = SDL_GetWindowSurface (_sdlWindow);

La deuxième fonction fondamentale sert uniquement à détruire proprement la fenêtre et quitter SDL avant de quitter le programme. SDL est un système qui doit initialiser mais aussi libérer la mémoire de ses éléments lorsqu’on souhaite partir. Pour ce faire, j’ai écris une petite fonction qui contient les deux fonctions de SDL qui gèrent tout ça.

1
2
3
4
void CleanUp ( void ) {
  SDL_DestroyWindow (_sdlWindow);
  SDL_Quit ();
}

Maintenant, pour que tout cela fonctionne, il faut appeler la fonction main du programme. Il n’y aura que deux choses à expliquer dedans. Voici le code.

1
2
3
4
5
6
7
8
9
10
11
12
int main ( void ) {
  Init ();
 
  SDL_FillRect (_sdlSurface, NULL, SDL_MapRGB (_sdlSurface->format, 0, 0, 0));
  SDL_UpdateWindowSurface (_sdlWindow);
 
  SDL_Delay (3000);
 
  CleanUp ();
 
  return 0;
}

Après avoir appelé ma fonction d’initialisation, on crée un rectangle de remplissage d’une seule couleur. Ici, j’ai choisis un fond noir. Cela se fait sur la surface, indépendamment de la fenêtre. C’est pour cela que la variable de la surface n’a pas été déclaré uniquement dans la fonction d’initialisation, mais en global comme la fenêtre. Il fallait pouvoir y accéder. Ensuite, on met à jour la fenêtre pour que la modification de la surface soit bien prise en compte.

Enfin, avant d’appeler l’arrêt de la fenêtre et de SDL à l’aide de ma fonction CleanUp(), j’ai mis une fonction de SDL qui permet de mettre le programme en pause pendant 3 secondes. Cette fonction est adapté à tout les systèmes d’exploitations. J’ai fais cela pour voir la fenêtre avant la fin du programme, étant donné qu’il n’y a pas d’événement de clavier et de boucle pour maintenir le programme et l’arrêter soi-même pour l’instant.

Voici le code complet du fichier main.cpp, en espérant que cet article vous aura intéressé et aura été clair. Je ne suis pas entrée dans les détails sur les fonctions ou les arguments, car là il s’agissait d’introduire la base pour utiliser SDL et afficher quelque chose à l’écran.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <includes.hpp>

SDL_Window* _sdlWindow;
SDL_Surface* _sdlSurface;

int Init ( void ) {
  _sdlWindow = NULL;
  _sdlSurface = NULL;
 
  if (SDL_Init (SDL_INIT_VIDEO) < 0) {
    printf ("SDL_Init, Error: %s", SDL_GetError ());
    return -1;
  }
 
  _sdlWindow = SDL_CreateWindow (
      "Simple window with SDL",
      SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
      720, 640,
      SDL_WINDOW_SHOWN);
 
  if (_sdlWindow == NULL) {
    printf ("SDL Create Window, Error: %s", SDL_GetError ());
    return -1;
  }
 
  _sdlSurface = SDL_GetWindowSurface (_sdlWindow);
  return 0;
}

void CleanUp ( void ) {
  SDL_DestroyWindow (_sdlWindow);
  SDL_Quit ();
}

int main ( void ) {
  Init ();
 
  SDL_FillRect (_sdlSurface, NULL, SDL_MapRGB (_sdlSurface->format, 0, 0, 0));
  SDL_UpdateWindowSurface (_sdlWindow);
 
  CleanUp ();
 
  return 0;
}

Si vous avez aimé cet article, vous pouvez me faire un don en Ethereum 🙂
Ethereum : 0xab7dD988aD7348C75db90343591596974A435803

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *