Форум программистов, компьютерный форум, киберфорум
8Observer8
Войти
Регистрация
Восстановить пароль

SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли в WebAssembly (Wasm)

Запись от 8Observer8 размещена 30.01.2026 в 23:45. Обновил(-а) 8Observer8 01.04.2026 в 14:01
Показов 4013 Комментарии 1

Содержание блога

Финальный код: finish-hello-wasm-sdl3-c.zip

Название: 693b6f07-c865-4613-88b4-b0e812a904bf.png
Просмотров: 7356

Размер: 2.0 Кб

QR-код для запуска на смартфонах:

Название: 010516df-4372-49e1-8b0a-ffcb04494b76.png
Просмотров: 7357

Размер: 4.6 Кб

Демка в браузере

Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples (кстати, они все на Си), то вы увидите, что все примеры используют следующие четыре обязательные функции, а привычная функция main() отсутствует:
  • SDL_AppInit (Initialization) - эта функция срабатывает один раз самой первой.
  • SDL_AppEvent (Event Handler) - эта функция срабатывает каждый раз, когда происходит какое-то событие, например, нажатие клавиши, движение мышью, клик мышью, изменение размеров окна и т.д.
  • SDL_AppIterate (Main Loop) - эта функция срабатывает множество раз в секунду. Здесь можно рассчитать dt (delta time) и сделать анимацию, скорость которой не будет зависеть от числа кадров в секунду, который поддерживает монитор.
  • SDL_AppQuit (Clean up) - эта функция сработает один раз перед закрытием приложения. Здесь нужно располагать код для освобождения ресурсов.

Эти функции позволяют собирать один и тот же код для Android, Desktop и WebAssembly без лишних #ifdef и дублирования логики - например, без специфичного для браузера цикла emscripten_set_main_loop при сборке через Emscripten. То есть кодовая база на SDL3-callbacks будет одинаковая для сборки в .exe, .apk, .wasm и т.д.

Рассмотрим пример 01-clear в файле clear.c. Я попросил Gemini 3 перевести комментарии на русский, не меняя ничего в коде, даже отступы:

C
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
 * Этот пример кода создает окно и рендерер SDL, а затем очищает
 * окно разным цветом в каждом кадре, так что вы фактически получите окно,
 * цвет которого плавно меняется.
 *
 * Этот код является общественным достоянием. Не стесняйтесь использовать его в любых целях!
 */
 
#define SDL_MAIN_USE_CALLBACKS 1  /* использовать функции обратного вызова вместо main() */
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
 
/* Мы будем использовать этот рендерер для отрисовки в этом окне в каждом кадре. */
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
 
/* Эта функция запускается один раз при запуске. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
    SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
    SDL_SetRenderLogicalPresentation(renderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX);
 
    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)
{
    const double now = ((double)SDL_GetTicks()) / 1000.0;  /* преобразуем миллисекунды в секунды. */
    /* выбираем цвет для кадра, который будем рисовать. Трюк с синусоидой заставляет цвета плавно сменять друг друга. */
    const float red = (float) (0.5 + 0.5 * SDL_sin(now));
    const float green = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 2 / 3));
    const float blue = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 4 / 3));
    SDL_SetRenderDrawColorFloat(renderer, red, green, blue, SDL_ALPHA_OPAQUE_FLOAT);  /* новый цвет, полная непрозрачность. */
 
    /* очищаем окно цветом отрисовки. */
    SDL_RenderClear(renderer);
 
    /* выводим свежеочищенный рендер на экран. */
    SDL_RenderPresent(renderer);
 
    return SDL_APP_CONTINUE;  /* продолжаем выполнение программы! */
}
 
/* Эта функция запускается один раз при завершении работы. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
    /* SDL сама очистит окно/рендерер за нас. */
}
Далее мы по шагам рассмотрим, как собрать этот пример в WASM, запустить в браузере локально и загрузить на бесплатный хостинг Vercel из командной строки.

Код стартового примера



  • Скачайте стартовый пример: start-hello-wasm-sdl3-c.zip
  • Код стартового примера:

    main.c

    C
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    #include <stdio.h>
    #include <emscripten.h>
     
    // Функция, которая будет вызываться браузером каждую итерацию (фрейм)
    void loop()
    {
        // Здесь обычно обрабатывается ввод пользователя или обновление кадра
    }
     
    int main()
    {
        printf("Привет из WebAssembly!\n");
     
        // Устанавливает главный цикл приложения. 
        // 0 — использовать стандартную частоту кадров браузера.
        // 1 — имитировать бесконечный цикл (предотвращает выход из main).
        emscripten_set_main_loop(loop, 0, 1); 
     
        return 0;
    }
    CMakeLists.txt

    Bash
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    cmake_minimum_required(VERSION 3.21)
    project(start-hello-wasm-sdl3-c)
     
    # Устанавливаем стандарт C (обязательно перед add_executable)
    set(CMAKE_C_STANDARD 17)
    set(CMAKE_C_STANDARD_REQUIRED ON)
     
    # Задаем название будущего приложения (в Windows это был бы app.exe, а
    # в вебе будет app.js / app.wasm)
    add_executable(app)
     
    # Добавляем исполняемый файл в сборку
    target_sources(app
    PRIVATE
        src/main.c
    )
    index.html

    PHP/HTML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    <!DOCTYPE html>
     
    <html>
     
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Hello Wasm</title>
    </head>
     
    <body>
        <script async src="./js/app.js"></script>
    </body>
  • Эта программа выводит строку "Привет из WebAssembly!" в консоль браузера. Функция main() не должна завершаться в браузере, поэтому используется бесконечный цикл emscripten_set_main_loop. Но мы не будет использовать эту функцию дальше. Мы будет использовать SDL3-callbacks, то есть 4 функции вместо main(), которые и на Desktop и в браузере
  • Примечание. При сборке в Release стартового примера выше у меня получилось приложение весом всего лишь 15.8 KB:
    • index.html - 245 bytes
    • app.js - 13.9 KB
    • app.wasm - 1.68 KB
    • Итог - 15.8 KB
    Название: 6f45f4bc-ddf5-43eb-88fe-9a9ef02fa610.png
Просмотров: 76

Размер: 2.9 Кб
  • На SDL3 будет около в районе 1 МБ, что тоже мало по современным меркам
  • Сделает сборку стартового примера по пошаговой инструкции ниже

Сборка стартового примера



  • Проверим работоспособность стартового примера, который мы скачали выше
  • Установите emsdk 4.0.15 по пошаговой инструкции: Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
  • После выполнения инструкции выше у вас будут установлены: CMake, emsdk, Node.js и http-server
  • Откройте CMD (консоль) в папке стартового примера
  • Введите следующие команды по очереди (это команды: конфигурирования, сборки и запуска локального сервера)
    Bash
    1
    2
    3
    
    config-web
    build-web
    http-server -c-1
  • Перейдите в браузер и введите адрес: localhost:8080 и нажмите Enter
  • Вы увидите результат работы программы в консоли браузера, если нажмёте: Ctrl+Shift+J в Chrome и Edge, либо Ctrl+Shift+K в FireFox
  • Запустите в корне папки проекта вторую консоль, чтобы дальше можно было переконфигурировать и собирать проект в этой консоли, а в первой консоли пусть запущен локальные сервер


Настройка инструментов



  • Скачайте архив SDL3-devel-3.4.2-wasm.zip
  • Примечание. Эту библиотеку я собрал из исходников с помощью Emscripten SDK 4.0.15 по инструкции: SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
  • Создайте на каком-нибудь локальном диске папку "libs", например, на диске C и разархивируйте скаченный архив в папку "libs":
    Нажмите на изображение для увеличения
Название: 931653e4-26ae-43ee-9938-f38e599e9330.png
Просмотров: 51
Размер:	3.0 Кб
ID:	11817
  • Откройте скаченный пример в каком-нибудь редакторе кода, например, Notepad++, а лучше в Sublime Text 4: https://www.sublimetext.com/download. Если вы добавите путь к Sublime Text 4 (ST4) в PATH, то сможете запускать ST4 из консоли командой subl . и тогда ST4 откроет текущую папку
  • Откройте файл CMakeLists.txt и скопируйте в него следующие настройки и не забудьте заменить путь к SDL3 на свой, если у вас путь отличается:

    CMakeLists.txt

    Bash
    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
    
    cmake_minimum_required(VERSION 3.21)
    project(start-hello-wasm-sdl3-c)
     
    # Устанавливаем стандарт C (обязательно перед add_executable)
    set(CMAKE_C_STANDARD 17)
    set(CMAKE_C_STANDARD_REQUIRED ON)
     
    # Задаем название будущего приложения (в Windows это был бы app.exe, а
    # в вебе будет app.js / app.wasm)
    add_executable(app)
     
    # Подсказываем CMake, где искать конфигурационные файлы библиотеки SDL3
    set(SDL3_DIR "C:/libs/SDL3-devel-3.4.2-wasm/lib/cmake/SDL3")
     
    # Загружаем настройки пакета SDL3 (параметры компиляции и пути к заголовкам)
    find_package(SDL3 REQUIRED)
     
    # Привязываем SDL3 к нашему приложению (настройка линковки и путей include)
    target_link_libraries(app PRIVATE SDL3::SDL3)
     
    # Добавляем исполняемый файл в сборку
    target_sources(app
    PRIVATE
        src/main.c
    )
  • Заменим этот код на код из официального примера, который был показан в начале этого сообщения (пример 01-clear), а в самом начале этого сообщение мы перевели комментарии на русский с помощью Gemini
  • Добавьте в код строку SDL_SetRenderVSync(renderer, 1); после создания окна:

    main.c

    C
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    
    /*
     * Этот пример кода создает окно и рендерер SDL, а затем очищает
     * окно разным цветом в каждом кадре, так что вы фактически получите окно,
     * цвет которого плавно меняется.
     *
     * Этот код является общественным достоянием. Не стесняйтесь использовать его в любых целях!
     */
     
    #define SDL_MAIN_USE_CALLBACKS 1 /* использовать функции обратного вызова вместо main() */
    #include <SDL3/SDL.h>
    #include <SDL3/SDL_main.h>
     
    /* Мы будем использовать этот рендерер для отрисовки в этом окне в каждом кадре. */
    static SDL_Window *window = NULL;
    static SDL_Renderer *renderer = NULL;
     
    /* Эта функция запускается один раз при запуске. */
    SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
    {
        SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear");
     
        if (!SDL_Init(SDL_INIT_VIDEO))
        {
            SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer))
        {
            SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
        SDL_SetRenderLogicalPresentation(renderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX);
     
        SDL_SetRenderVSync(renderer, 1);
     
        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)
    {
        const double now = ((double)SDL_GetTicks()) / 1000.0; /* преобразуем миллисекунды в секунды. */
        /* выбираем цвет для кадра, который будем рисовать. Трюк с синусоидой заставляет цвета плавно сменять друг друга. */
        const float red = (float)(0.5 + 0.5 * SDL_sin(now));
        const float green = (float)(0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 2 / 3));
        const float blue = (float)(0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 4 / 3));
        SDL_SetRenderDrawColorFloat(renderer, red, green, blue, SDL_ALPHA_OPAQUE_FLOAT); /* новый цвет, полная непрозрачность. */
     
        /* очищаем окно цветом отрисовки. */
        SDL_RenderClear(renderer);
     
        /* выводим свежеочищенный рендер на экран. */
        SDL_RenderPresent(renderer);
     
        return SDL_APP_CONTINUE; /* продолжаем выполнение программы! */
    }
     
    /* Эта функция запускается один раз при завершении работы. */
    void SDL_AppQuit(void *appstate, SDL_AppResult result)
    {
        /* SDL сама очистит окно/рендерер за нас. */
    }
  • Примечание. Вы можете отформатировать одой командой из консоли, то есть автоматически расставить отступы с помощью пошаговой инструкции: Консольные команды для форматирования исходного кода на C, C++, C#, Java, JavaScript, HTML и CSS. Сортировка пакетов на Python
  • Откройте в редакторе кода файл "public/index.html" и замените его содержимое на следующее:

    index.html

    PHP/HTML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    <!DOCTYPE html>
     
    <html>
     
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
     
    <body>
        <canvas id="canvas"></canvas>
     
        <script async src="./js/app.js"></script>
    </body>
     
    </html>
  • Введите команды конфигурирования и сборки (на самом деле можно команду build-web, а конфигурирование произойдёт по автомату, потому что CMake автоматически проверяет не был ли изменён файл CMakeLists.txt)
    Bash
    1
    2
    
    config-web
    build-web
  • Перейдите в браузер и введите адрес: localhost:8080 и нажмите Enter
  • Обратите внимание, что локальный сервер http-server выводит адреса, например, у меня:
    Bash
    1
    2
    3
    4
    5
    
    Available on:
      http://100.116.245.193:8080
      http://192.168.56.1:8080
      http://127.0.0.1:8080
    Hit CTRL-C to stop the server
  • Вы можете взять телефон, который подключён к Wi-Fi и ввести в его браузере адрес 192.168.56.1:8080 и приложение запустится на Android
  • Загрузите созданное веб-приложение (папку public на бесплатный хостинг Vercel по следующей инструкции

Сборка в Release и вес SDL3 веб-приложения



При сборке в релиз нужно в файле config-web.bat заменить Debug на Release]:

config-web.bat

Bash
1
emcmake cmake -S . -B dist -DCMAKE_BUILD_TYPE=Release
После этого нужно ввести команду config-web. Собирается медленнее секунд на 10 секунд, чем в Debug, в зависимости от мощности компьютера. В результате у меня получилось приложение весом 982 KB (папка public). Папку public мы будет отгружать на бесплатный хостинг в следующей инструкции

Загрузка веб-приложения на бесплатный хостинг Vercel



  • Зарегистрируйтесь в Vercel: https://vercel.com/
    Установите Vercel глобально командой:
    Bash
    1
    
    npm i -g vercel
  • В корне проекта создайте файл с именем "vercel.json" со следующим содержимым:

    vercel.json

    JSON
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    {
      "headers": [
        {
          "source": "/(.*)",
          "headers": [
            { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
            { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
          ]
        }
      ]
    }
  • Примечание. Этот файл нужен для загрузки файла app.wasm
  • В консоли из корня проекта выполните команду для входа в Vercel:
    Bash
    1
    
    vercel login
  • В консоли вы увидите сообщение: "Press [ENTER] to open the browser" ("Нажмите [ENTER], чтобы перейти в браузер")
  • Нажмите Enter и вы будете перенаправлены в браузер, где нужно будет нажать кнопку "Allow" ("Позволяю")
  • Наберите команду для первой отгрузки приложения на хостинг:
    Bash
    1
    
    vercel
  • В консоли будут задаваться вопросы и на все эти вопросы Enter
  • Вы будет выдан адрес вашего приложения:
    Code
    1
    
    Aliased: https://finish-hello-wasm-sdl3-c.vercel.app
  • Примечание. К сожалению, иногда Vercel часто становится недоступным в РФ. Как альтернатива, можете загрузить папку public на бесплатный хостинг GitHub Pages. Я загрузил финальный результат на GitHub Pages -> демка в браузере

Создание QR-кода для быстрого запуска на смартфонах



Мы собрали приложение и загрузили его на бесплатный хостинг. У нас есть ссылка на приложение. Используем эту ссылку, чтобы создать QR-код

  • Наберите в поисковике интернета: qr code generator
  • Одна из первых ссылок будет указывать на ресурс, на который нужно перейти: https://www.qr-code-generator.com/
  • В поле ввода скопируйте ссылку на приложение. QR-код появится справа
  • Внизу выберите какой-нибудь способ отображение QR-кода. Нарпимер:
    Название: 010516df-4372-49e1-8b0a-ffcb04494b76.png
Просмотров: 7357

Размер: 4.6 Кб
Вложения
Тип файла: zip start-hello-wasm-sdl3-c.zip (3.2 Кб, 29 просмотров)
Тип файла: zip SDL3-devel-3.4.2-wasm.zip (1.22 Мб, 47 просмотров)
Тип файла: zip finish-hello-wasm-sdl3-c.zip (4.5 Кб, 146 просмотров)
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 1
Комментарии
  1. Старый комментарий
    Обновил c SDL 3.4.0 до SDL 3.4.2
    Запись от 8Observer8 размещена 31.03.2026 в 01:10 8Observer8 вне форума
    Обновил(-а) 8Observer8 31.03.2026 в 04:57
 
Новые блоги и статьи
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru