Aller au contenu

SDL2 Cheat Sheet

Overview

SDL (Simple DirectMedia Layer) is a cross-platform development library designed to provide low-level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL, Vulkan, Metal, and Direct3D. SDL 2 is written in C and works natively with C++, and has bindings for many other languages including Python (pygame), Rust, Go, and C#. It is widely used for game development, emulators, media players, and any application requiring direct hardware access.

SDL2 handles the platform-specific details of creating windows, processing input events, playing audio, and setting up rendering contexts, while remaining thin enough to give developers fine-grained control. Major games and engines built on SDL include Valve’s Source engine ports, Unreal Engine (for Linux/macOS), countless indie games, and emulators like RetroArch. The library supports Windows, macOS, Linux, iOS, Android, and several other platforms. Companion libraries SDL_image, SDL_ttf, SDL_mixer, and SDL_net extend its capabilities.

Installation

# macOS
brew install sdl2 sdl2_image sdl2_ttf sdl2_mixer sdl2_net

# Ubuntu/Debian
sudo apt install libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev \
  libsdl2-mixer-dev libsdl2-net-dev

# Fedora
sudo dnf install SDL2-devel SDL2_image-devel SDL2_ttf-devel \
  SDL2_mixer-devel SDL2_net-devel

# Arch Linux
sudo pacman -S sdl2 sdl2_image sdl2_ttf sdl2_mixer sdl2_net

# Windows (vcpkg)
vcpkg install sdl2 sdl2-image sdl2-ttf sdl2-mixer

# Verify
sdl2-config --version
sdl2-config --cflags --libs

Basic Window and Renderer

// main.c
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdbool.h>

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600

int main(int argc, char* argv[])
{
    // Initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
        printf("SDL Init failed: %s\n", SDL_GetError());
        return 1;
    }

    // Create window
    SDL_Window* window = SDL_CreateWindow(
        "My SDL2 Game",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
    );
    if (!window) {
        printf("Window creation failed: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    // Create renderer
    SDL_Renderer* renderer = SDL_CreateRenderer(
        window, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
    );
    if (!renderer) {
        printf("Renderer creation failed: %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    // Game loop
    bool running = true;
    SDL_Event event;
    Uint32 lastTime = SDL_GetTicks();

    while (running) {
        // Delta time
        Uint32 currentTime = SDL_GetTicks();
        float deltaTime = (currentTime - lastTime) / 1000.0f;
        lastTime = currentTime;

        // Event handling
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = false;
            }
            if (event.type == SDL_KEYDOWN) {
                if (event.key.keysym.sym == SDLK_ESCAPE) {
                    running = false;
                }
            }
        }

        // Update game state
        // (game logic here)

        // Render
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        // Draw stuff here

        SDL_RenderPresent(renderer);
    }

    // Cleanup
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Drawing Primitives

// Set draw color (RGBA)
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);  // Red

// Draw rectangle
SDL_Rect rect = { 100, 100, 200, 150 };
SDL_RenderFillRect(renderer, &rect);    // Filled
SDL_RenderDrawRect(renderer, &rect);    // Outline

// Draw line
SDL_RenderDrawLine(renderer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

// Draw point
SDL_RenderDrawPoint(renderer, 400, 300);

// Draw multiple lines
SDL_Point points[] = {
    { 100, 100 }, { 200, 50 }, { 300, 100 }, { 200, 150 }, { 100, 100 }
};
SDL_RenderDrawLines(renderer, points, 5);

// Set blend mode for transparency
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 128);  // Semi-transparent blue
SDL_RenderFillRect(renderer, &rect);

Textures and Images

#include <SDL2/SDL_image.h>

// Initialize SDL_image
IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG);

// Load texture from file
SDL_Texture* texture = IMG_LoadTexture(renderer, "sprites/player.png");
if (!texture) {
    printf("Texture load failed: %s\n", IMG_GetError());
}

// Get texture dimensions
int texW, texH;
SDL_QueryTexture(texture, NULL, NULL, &texW, &texH);

// Draw texture
SDL_Rect destRect = { x, y, texW, texH };
SDL_RenderCopy(renderer, texture, NULL, &destRect);

// Draw portion of texture (spritesheet)
SDL_Rect srcRect = { frameX * 32, frameY * 32, 32, 32 };  // Source region
SDL_Rect dstRect = { playerX, playerY, 64, 64 };           // Destination (scaled)
SDL_RenderCopy(renderer, spritesheet, &srcRect, &dstRect);

// Draw with rotation and flip
SDL_RenderCopyEx(renderer, texture, &srcRect, &dstRect,
    angle,      // Rotation in degrees
    NULL,       // Center of rotation (NULL = center)
    SDL_FLIP_HORIZONTAL  // Flip mode
);

// Set texture color/alpha modulation
SDL_SetTextureColorMod(texture, 255, 128, 128);  // Tint
SDL_SetTextureAlphaMod(texture, 200);             // Transparency
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);

// Create texture from surface
SDL_Surface* surface = SDL_CreateRGBSurface(0, 100, 100, 32, 0, 0, 0, 0);
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 0, 0));
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);

// Cleanup
SDL_DestroyTexture(texture);
IMG_Quit();

Input Handling

// Event-based input
SDL_Event event;
while (SDL_PollEvent(&event)) {
    switch (event.type) {
        case SDL_QUIT:
            running = false;
            break;
        
        case SDL_KEYDOWN:
            switch (event.key.keysym.sym) {
                case SDLK_SPACE: jump(); break;
                case SDLK_ESCAPE: running = false; break;
                case SDLK_F11: toggleFullscreen(); break;
            }
            break;
        
        case SDL_KEYUP:
            if (event.key.keysym.sym == SDLK_SPACE)
                stopJumping();
            break;
        
        case SDL_MOUSEBUTTONDOWN:
            if (event.button.button == SDL_BUTTON_LEFT)
                shoot(event.button.x, event.button.y);
            break;
        
        case SDL_MOUSEMOTION:
            mouseX = event.motion.x;
            mouseY = event.motion.y;
            break;
        
        case SDL_MOUSEWHEEL:
            zoom += event.wheel.y;
            break;
        
        case SDL_WINDOWEVENT:
            if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
                screenW = event.window.data1;
                screenH = event.window.data2;
            }
            break;
    }
}

// Polling-based keyboard state (for continuous movement)
const Uint8* keystate = SDL_GetKeyboardState(NULL);
if (keystate[SDL_SCANCODE_LEFT] || keystate[SDL_SCANCODE_A])
    playerX -= speed * deltaTime;
if (keystate[SDL_SCANCODE_RIGHT] || keystate[SDL_SCANCODE_D])
    playerX += speed * deltaTime;
if (keystate[SDL_SCANCODE_UP] || keystate[SDL_SCANCODE_W])
    playerY -= speed * deltaTime;
if (keystate[SDL_SCANCODE_DOWN] || keystate[SDL_SCANCODE_S])
    playerY += speed * deltaTime;

// Mouse state polling
int mx, my;
Uint32 buttons = SDL_GetMouseState(&mx, &my);
if (buttons & SDL_BUTTON_LMASK) {
    // Left button held
}

Audio

#include <SDL2/SDL_mixer.h>

// Initialize audio
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);

// Load sound effect
Mix_Chunk* jumpSound = Mix_LoadWAV("sounds/jump.wav");
Mix_Chunk* explosionSound = Mix_LoadWAV("sounds/explosion.wav");

// Play sound effect
Mix_PlayChannel(-1, jumpSound, 0);  // -1 = first free channel, 0 = no loop
Mix_PlayChannel(1, explosionSound, 0);  // Play on channel 1

// Set volume (0-128)
Mix_VolumeChunk(jumpSound, 64);  // Half volume

// Load and play music
Mix_Music* music = Mix_LoadMUS("music/background.mp3");
Mix_PlayMusic(music, -1);  // -1 = loop forever
Mix_VolumeMusic(32);       // Set music volume

// Music controls
Mix_PauseMusic();
Mix_ResumeMusic();
Mix_HaltMusic();
Mix_FadeOutMusic(2000);  // Fade out over 2 seconds

// Cleanup
Mix_FreeChunk(jumpSound);
Mix_FreeChunk(explosionSound);
Mix_FreeMusic(music);
Mix_CloseAudio();

Text Rendering

#include <SDL2/SDL_ttf.h>

// Initialize
TTF_Init();

// Load font
TTF_Font* font = TTF_OpenFont("fonts/arial.ttf", 24);
TTF_Font* titleFont = TTF_OpenFont("fonts/bold.ttf", 48);

// Render text to surface then texture
SDL_Color white = { 255, 255, 255, 255 };
SDL_Surface* textSurface = TTF_RenderText_Blended(font, "Score: 100", white);
SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

int texW = textSurface->w;
int texH = textSurface->h;
SDL_FreeSurface(textSurface);

// Draw text
SDL_Rect textRect = { 10, 10, texW, texH };
SDL_RenderCopy(renderer, textTexture, NULL, &textRect);

// Render methods:
// TTF_RenderText_Solid()   - Fast, no anti-aliasing
// TTF_RenderText_Shaded()  - Anti-aliased with background color
// TTF_RenderText_Blended() - Anti-aliased with alpha (best quality)

// Cleanup
SDL_DestroyTexture(textTexture);
TTF_CloseFont(font);
TTF_Quit();

Gamepad/Joystick

// Initialize
SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK);

// Open first gamepad
SDL_GameController* controller = NULL;
for (int i = 0; i < SDL_NumJoysticks(); i++) {
    if (SDL_IsGameController(i)) {
        controller = SDL_GameControllerOpen(i);
        printf("Controller: %s\n", SDL_GameControllerName(controller));
        break;
    }
}

// Read gamepad in update loop
if (controller) {
    // Analog sticks (-32768 to 32767)
    int leftX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
    int leftY = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
    
    // Apply deadzone
    float lx = (abs(leftX) > 8000) ? leftX / 32767.0f : 0;
    float ly = (abs(leftY) > 8000) ? leftY / 32767.0f : 0;
    
    // Buttons
    if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A))
        jump();
    if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START))
        pause();
    
    // Triggers (0 to 32767)
    int rightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
}

// Cleanup
SDL_GameControllerClose(controller);

Configuration (Build)

# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(mygame LANGUAGES C CXX)

set(CMAKE_C_STANDARD 11)

find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(SDL2_ttf REQUIRED)
find_package(SDL2_mixer REQUIRED)

add_executable(mygame
    src/main.c
    src/game.c
    src/player.c
    src/renderer.c
)

target_link_libraries(mygame PRIVATE
    SDL2::SDL2
    SDL2_image::SDL2_image
    SDL2_ttf::SDL2_ttf
    SDL2_mixer::SDL2_mixer
)
# Build with CMake
mkdir build && cd build
cmake ..
cmake --build .

# Build with gcc directly
gcc -o mygame main.c $(sdl2-config --cflags --libs) -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lm

# Build with pkg-config
gcc -o mygame main.c $(pkg-config --cflags --libs sdl2 SDL2_image SDL2_ttf SDL2_mixer)

Advanced Usage

// Render to texture (framebuffer)
SDL_Texture* target = SDL_CreateTexture(renderer,
    SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 256, 256);

SDL_SetRenderTarget(renderer, target);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
// Draw to texture...
SDL_SetRenderTarget(renderer, NULL);  // Reset to screen
SDL_RenderCopy(renderer, target, NULL, &destRect);

// Fullscreen toggle
void toggleFullscreen(SDL_Window* window) {
    Uint32 flags = SDL_GetWindowFlags(window);
    if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
        SDL_SetWindowFullscreen(window, 0);
    } else {
        SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
    }
}

// Pixel manipulation
SDL_Surface* surface = SDL_CreateRGBSurface(0, 256, 256, 32,
    0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
SDL_LockSurface(surface);
Uint32* pixels = (Uint32*)surface->pixels;
for (int y = 0; y < 256; y++) {
    for (int x = 0; x < 256; x++) {
        pixels[y * 256 + x] = SDL_MapRGBA(surface->format, x, y, 128, 255);
    }
}
SDL_UnlockSurface(surface);

// High-performance timing
Uint64 perfFreq = SDL_GetPerformanceFrequency();
Uint64 startTime = SDL_GetPerformanceCounter();
// ... work ...
Uint64 endTime = SDL_GetPerformanceCounter();
double elapsed = (double)(endTime - startTime) / (double)perfFreq;

// Clipboard
SDL_SetClipboardText("Copied text");
char* text = SDL_GetClipboardText();
SDL_free(text);

Troubleshooting

IssueSolution
Black window / no renderingCheck SDL_RenderPresent() called; verify renderer created
”SDL_Init failed”Check SDL2 installed; verify shared libraries in path
Texture loading failsCheck file path; ensure SDL_image initialized with correct formats
No audio outputVerify Mix_OpenAudio() succeeded; check audio device availability
Tearing / stutteringEnable VSync: SDL_RENDERER_PRESENTVSYNC; use delta time
High CPU usageUse SDL_Delay() or VSync to cap frame rate
Controller not detectedCall SDL_Init(SDL_INIT_GAMECONTROLLER); check controller database
Memory leakDestroy all textures, surfaces, fonts; call SDL_Quit() subsystems
Blurry renderingSet SDL_HINT_RENDER_SCALE_QUALITY to “0” for pixel art
Window not respondingAlways call SDL_PollEvent() in main loop to process OS events