SDL¤
SDL (Simple DirectMedia Layer) est une bibliothèque multiplateforme qui permet de créer des applications graphiques. Elle est utilisée dans de nombreux jeux vidéo et applications multimédia. Elle est écrite en C, mais des bindings existent pour de nombreux langages, dont Python ou C++.
La biblithèque a été créée en 1998 par Sam Lantinga, un développeur alors employé par Loki Software, une société spécialisée dans le portage de jeux vidéo sur Linux. À l’époque, il était difficile pour les développeurs de créer des jeux multiplateformes, car chaque système d'exploitation (Windows, Linux, macOS) utilisait des bibliothèques et des API différentes pour gérer les graphismes, le son et les entrées. SDL a été développée pour offrir une interface unique et simple permettant d'accéder à ces fonctionnalités sur plusieurs plateformes, facilitant ainsi le développement de jeux et d'applications multimédias multiplateformes.
On peut citer quelques projets qui utilisent SDL.
La plupart de ces projets sont développés en C++ car le C n'est pas un langage très adapté pour le développement d'applications complexes et graphiques.
La bibliothèque est plus qu'une simple abstraction des fonctionnalités graphiques des systèmes d'exploitation. Elle offre également des fonctionnalités notament :
- Gestion des fenêtres
- Gestion des événements (clavier, souris, joystick)
- Gestion du son
- Multi-threading
- Timers
- Accélération 2D
- Support 3D (Vulkan, Metal, OpenGL)
La bibliothèque est plus complète que GLFW et est bien adaptée à des projets complexes en C.
Premiers pas¤
Pour installer SDL sur votre système, vous pouvez utiliser le gestionnaire de paquets de votre distribution. Sous Ubuntu :
Une extension de SDL nommée GFX est également disponible. Elle ajoute des fonctionnalités graphiques supplémentaires comme des fonctions de dessin de primitives (lignes, rectangles, cercles, etc.).
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <stdbool.h>
#include <stdio.h>
#define S(i) (sin(2 * M_PI / 3 * i))
#define C(i) (cos(2 * M_PI / 3 * i))
int main(int argc, char *argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Error: SDL Initialization: %s\n", SDL_GetError());
exit(1);
}
SDL_Window *window =
SDL_CreateWindow("Circles", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, 800, 400, SDL_WINDOW_SHOWN);
if (!window) {
fprintf(stderr, "Error: CreateWindow: %s\n", SDL_GetError());
exit(2);
}
SDL_Renderer *r = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!r) {
fprintf(stderr, "Error: Renderer: %s\n", SDL_GetError());
exit(3);
}
SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_ADD);
bool running = true;
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT) running = false;
SDL_SetRenderDrawColor(r, 255, 255, 255, 255);
SDL_RenderClear(r);
Sint16 cx = 400, cy = 200, d = 50;
filledCircleRGBA(r, cx + d * C(1), cy + d * S(1), 100, 255, 0, 0, 100);
filledCircleRGBA(r, cx + d * C(2), cy + d * S(2), 100, 0, 255, 0, 100);
filledCircleRGBA(r, cx + d * C(3), cy + d * S(3), 100, 0, 0, 255, 100);
SDL_RenderPresent(r);
}
SDL_DestroyRenderer(r);
SDL_DestroyWindow(window);
SDL_Quit();
}
Polygones¤
Voici un exemple d'un programme de dessin de polygones en utilisant SDL.
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h> // Inclure SDL2_gfx
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#define GRIDSTEP 10
#define MAX_POINTS 100
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 400;
const SDL_Color GRID_COLOR = {80, 80, 80, 200};
const SDL_Color BACKGROUND_COLOR = {30, 30, 30, 255};
const SDL_Color LINE_COLOR = {255, 0, 128, 255};
const SDL_Color ANCHOR_COLOR = {0, 255, 255, 150};
typedef struct Point {
float x, y;
} Point;
typedef struct Segment {
Point p, q;
} Segment;
typedef struct State {
bool drawing;
Point start, last, current;
size_t segment_count;
Segment segments[MAX_POINTS];
} State;
#define COLORARGS(color) color.r, color.g, color.b, color.a
void clear(SDL_Renderer *renderer) {
SDL_SetRenderDrawColor(renderer, COLORARGS(BACKGROUND_COLOR));
SDL_RenderClear(renderer);
}
void drawGrid(SDL_Renderer *renderer) {
for (int x = 0; x < WINDOW_WIDTH; x += GRIDSTEP) {
SDL_Color color = GRID_COLOR;
color.a = x % 100 ? 100 : 200;
SDL_SetRenderDrawColor(renderer, COLORARGS(color));
SDL_RenderDrawLine(renderer, x, 0, x, WINDOW_HEIGHT);
}
for (int y = 0; y < WINDOW_HEIGHT; y += GRIDSTEP) {
SDL_Color color = GRID_COLOR;
color.a = y % 100 ? 100 : 200;
SDL_SetRenderDrawColor(renderer, COLORARGS(color));
SDL_RenderDrawLine(renderer, 0, y, WINDOW_WIDTH, y);
}
}
void drawAnchor(SDL_Renderer *r, Point p, SDL_Color color) {
const int size = 6, half = size / 2;
SDL_SetRenderDrawColor(r, COLORARGS(color));
SDL_RenderFillRect(r, &(SDL_Rect){p.x - half, p.y - half, size, size});
}
void drawSegment(SDL_Renderer *r, Segment s) {
SDL_SetRenderDrawColor(r, COLORARGS(LINE_COLOR));
SDL_RenderDrawLine(r, s.p.x, s.p.y, s.q.x, s.q.y);
drawAnchor(r, s.p, ANCHOR_COLOR);
drawAnchor(r, s.q, ANCHOR_COLOR);
}
void drawSegments(SDL_Renderer *renderer, Segment *segments, size_t count) {
for (int i = 0; i < count; i++) drawSegment(renderer, segments[i]);
}
void addSegment(State *state) {
if (state->segment_count >= MAX_POINTS) {
fprintf(stderr, "Reached maximum number of points\n");
return;
}
state->segments[state->segment_count++] =
(Segment){.p = state->last, .q = state->current};
}
void endSegment(State *state) {
state->drawing = false;
addSegment(state);
}
bool isClose(Point p, Point q, int threshold) {
return (fabs(p.x - q.x) < threshold) && (fabs(p.y - q.y) < threshold);
}
bool onStartAnchor(State state) {
return isClose(state.start, state.current, 10);
}
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Polygons", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH,
WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer =
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
State state = {0};
bool running = true;
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
continue;
}
if (event.type == SDL_MOUSEMOTION) {
state.current = (Point){event.button.x, event.button.y};
continue;
}
if (event.type == SDL_MOUSEBUTTONDOWN &&
event.button.button == SDL_BUTTON_LEFT) {
if (!state.drawing) {
state.drawing = true;
state.start = state.current;
} else if (onStartAnchor(state)) {
state.current = state.start;
endSegment(&state);
} else {
addSegment(&state);
}
state.last = state.current;
}
}
clear(renderer);
drawGrid(renderer);
drawSegments(renderer, state.segments, state.segment_count);
if (state.start.x >= 0 && state.start.y >= 0)
drawAnchor(renderer, state.start, ANCHOR_COLOR);
if (state.drawing)
drawSegment(renderer, (Segment){.p = state.last, .q = state.current});
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}