Integrating SDL3 Library with CMake on Windows & Ubuntu (OpenGL 3.3) Интеграция C++-библиотеки SDL3 с CMake под Windows и Ubuntu (OpenGL 3.3)

A step-by-step guide to configuring and integrating the SDL3 library and an OpenGL 3.3 context in your CMake projects on Windows and Ubuntu. Пошаговое руководство по настройке и интеграции библиотеки SDL3 и контекста OpenGL 3.3 в проекты CMake под Windows и Ubuntu.

Before proceeding, make sure you have built and installed the static libraries on your system using the appropriate guides: Перед продолжением убедитесь, что вы собрали и установили статические библиотеки в вашей системе, следуя соответствующим инструкциям:

Step 1: Create Project Directory Шаг 1: Создание директории проекта

Create a workspace folder and a new directory for your SDL3 OpenGL test project: Создайте рабочую папку для проектов и новую директорию для вашего тестового проекта SDL3 OpenGL:

Windows (Command Prompt / PowerShell)

mkdir C:\projects\sdl3-opengl-test
cd C:\projects\sdl3-opengl-test

Ubuntu (Terminal)

mkdir -p ~/projects/sdl3-opengl-test
cd ~/projects/sdl3-opengl-test

Step 2: Create Source Files (main.cpp & CMakeLists.txt) Шаг 2: Создание исходных файлов (main.cpp и CMakeLists.txt)

You can create these files using Notepad / Sublime Text on Windows, or nano in Ubuntu terminal: Вы можете создать эти файлы через Блокнот / Sublime Text в Windows или с помощью nano в терминале Ubuntu:

Windows (Terminal / Notepad)

notepad main.cpp
notepad CMakeLists.txt

Ubuntu (Terminal / Nano)

nano main.cpp
nano CMakeLists.txt
Quick Nano Commands (Ubuntu):Быстрые команды Nano (Ubuntu): Paste code with Right Mouse Button (RMB) → Press Ctrl + O and Enter to save → Press Ctrl + X to exit. Вставьте код правой кнопкой мыши (ПКМ) → Нажмите Ctrl + O и Enter для сохранения → Нажмите Ctrl + X для выхода.

1A. File Contents for main.cpp (Classic Loop) 1A. Содержимое файла main.cpp (Классический цикл)

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_opengl.h>

int main(int argc, char* argv[]) {
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("SDL Initialization failed: %s", SDL_GetError());
        return -1;
    }

    // Configure OpenGL 3.3 Core Profile attributes
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_Window* window = SDL_CreateWindow("SDL3 OpenGL 3.3 Window (Classic)", 800, 450, SDL_WINDOW_OPENGL);
    if (!window) {
        SDL_Log("Window creation failed: %s", SDL_GetError());
        SDL_Quit();
        return -1;
    }

    SDL_GLContext glContext = SDL_GL_CreateContext(window);
    if (!glContext) {
        SDL_Log("OpenGL context creation failed: %s", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return -1;
    }

    SDL_GL_MakeCurrent(window, glContext);
    SDL_GL_SetSwapInterval(1); // Enable VSync

    bool running = true;
    while (running) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_EVENT_QUIT) {
                running = false;
            }
        }

        // Clear screen with OpenGL 3.3 clear color
        glClearColor(0.9f, 0.96f, 0.92f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        SDL_GL_SwapWindow(window);
    }

    SDL_GL_DestroyContext(glContext);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

1B. File Contents for main.cpp (SDL3 Callbacks API) 1B. Содержимое файла main.cpp (SDL3 Callbacks API)

#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_opengl.h>

struct AppState {
    SDL_Window* window = nullptr;
    SDL_GLContext glContext = nullptr;
};

SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("SDL Initialization failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    // Configure OpenGL 3.3 Core Profile attributes
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    AppState* state = new AppState();
    *appstate = state;

    state->window = SDL_CreateWindow("SDL3 OpenGL 3.3 Window (Callbacks)", 800, 450, SDL_WINDOW_OPENGL);
    if (!state->window) {
        SDL_Log("Window creation failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    state->glContext = SDL_GL_CreateContext(state->window);
    if (!state->glContext) {
        SDL_Log("OpenGL context creation failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    SDL_GL_MakeCurrent(state->window, state->glContext);
    SDL_GL_SetSwapInterval(1); // Enable VSync

    return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
    if (event->type == SDL_EVENT_QUIT) {
        return SDL_APP_SUCCESS;
    }
    return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppIterate(void* appstate) {
    auto* state = static_cast<AppState*>(appstate);

    // Clear screen with OpenGL 3.3 clear color
    glClearColor(0.9f, 0.96f, 0.92f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    SDL_GL_SwapWindow(state->window);

    return SDL_APP_CONTINUE;
}

void SDL_AppQuit(void* appstate, SDL_AppResult result) {
    if (appstate) {
        auto* state = static_cast<AppState*>(appstate);
        if (state->glContext) {
            SDL_GL_DestroyContext(state->glContext);
        }
        if (state->window) {
            SDL_DestroyWindow(state->window);
        }
        delete state;
    }
    SDL_Quit();
}
SDL3 Note: Classic Loop vs. Callbacks API Заметка SDL3: Классический цикл vs. Callbacks API

Option 1A uses the traditional main() event loop. Option 1B uses the new SDL3 recommended Callbacks API (via SDL_MAIN_USE_CALLBACKS). The Callbacks API hands main loop control over to the OS, preventing window rendering freezes during drag/resize operations and enabling seamless cross-platform support across WebAssembly, iOS, and Android. Вариант 1A использует традиционный цикл событий main(). Вариант 1B использует новый рекомендуемый в SDL3 Callbacks API (через SDL_MAIN_USE_CALLBACKS). Callbacks API передаёт управление основным циклом операционной системе, что предотвращает зависание рендеринга при перетаскивании или изменении размера окна и обеспечивает нативную кроссплатформенность для WebAssembly, iOS и Android.

2. File Contents for CMakeLists.txt 2. Содержимое файла CMakeLists.txt

cmake_minimum_required(VERSION 3.21)

# Default to Debug if CMAKE_BUILD_TYPE is not set via command line
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()

project(sdl3_opengl_test CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# -----------------------------------------------------------------------------
# Locate System OpenGL Package
# -----------------------------------------------------------------------------
find_package(OpenGL REQUIRED)

# -----------------------------------------------------------------------------
# Locate SDL3 Package via Custom Paths
# -----------------------------------------------------------------------------
if(WIN32)
    # Points to the directory containing SDL3Config.cmake
    set(SDL3_DIR "C:/libs/sdl3-3.4.16-mingw/lib/cmake/SDL3")
else()
    set(SDL3_DIR "$ENV{HOME}/libs/sdl3-3.4.16-ubuntu/lib/cmake/SDL3")
endif()

find_package(SDL3 REQUIRED CONFIG)

# -----------------------------------------------------------------------------
# Target Definition
# -----------------------------------------------------------------------------
set(PROJECT_SOURCES
    main.cpp
)

add_executable(app ${PROJECT_SOURCES})

# Link SDL3 statically and system OpenGL library
target_link_libraries(app PRIVATE SDL3::SDL3-static OpenGL::GL)

# -----------------------------------------------------------------------------
# Platform-Specific Compiler & Linker Flags
# -----------------------------------------------------------------------------
if(WIN32)
    target_compile_options(app PRIVATE -Os)
    target_link_options(app PRIVATE -static -s -static-libgcc)

    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        target_link_options(app PRIVATE -mconsole)
    else()
        target_link_options(app PRIVATE -mwindows)
    endif()
endif()

Step 3: Create Build & Execution Scripts Шаг 3: Создание скриптов сборки и запуска

Windows (MinGW / Batch Scripts)

config-exe.bat

cmake -G Ninja -S . -B dist/win

build-exe.bat

cmake --build dist/win

run-exe.bat

@echo off
.\dist\win\app.exe

Run the scripts sequentially in Command Prompt or PowerShell: Последовательно запустите скрипты в командной строке или PowerShell:

config-exe.bat
build-exe.bat
run-exe.bat

Ubuntu (Bash Scripts)

config-bin.sh

#!/usr/bin/env bash
set -e
cmake -G Ninja -S . -B dist/linux

build-bin.sh

#!/usr/bin/env bash
set -e
cmake --build dist/linux

run-bin.sh

#!/usr/bin/env bash
set -e
./dist/linux/app

Grant execute permissions on Ubuntu before running: Предоставьте права на выполнение в Ubuntu перед запуском:

chmod +x config-bin.sh build-bin.sh run-bin.sh
./config-bin.sh
./build-bin.sh
./run-bin.sh

Step 4: Expected Output Шаг 4: Ожидаемый результат

After running the build and execution scripts, a window should appear displaying: После запуска скриптов сборки и выполнения появится окно с надписью:

SDL3 OpenGL 3.3 Window (Classic)

or (if using the Callbacks API version): или (если используется версия с Callbacks API):

SDL3 OpenGL 3.3 Window (Callbacks)

Support My Work Поддержать проект

If these tutorials helped you, consider buying me a coffee! Если эти туториалы вам помогли, вы можете поддержать автора.

Sberbank (Russia only) Сбербанк (только для РФ)

Sberbank SBP QR Code

Direct transfer via phone number (Russia only) Перевод по номеру телефона (только для РФ)

+7 (917) 212-29-59

USDT TRC20

USDT TRC20 QR Code

Support via Cryptocurrency Поддержка криптовалютой

TMtY1YifNf6FKvgeFmqKGQR4NStKr3csGp