Содержание блога
Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик касания экрана мобильного устройства, чтобы один и тот же проект работал в браузере ПК и в браузере мобильного устройства. Исходники того, что получилось: mouse-and-touch-handlers-sdl3.zip
Если у вас не установлены Emscripten SDK и CMake, то установите их по следующей инструкции и выполните "hello world" инструкцию там же: Установка Emscripten SDK (emsdk) и CMake
Установка библиотеки SDL3 для Wasm
- Скачайте SDL3 для Wasm: SDL3-devel-3.4.0-wasm.zip Библиотека была собрана из исходников с помощью emsdk3 4.0.15
- Создайте пустую папку с именем "libs", например, на диске C
- Извлеките архив с библиотекой в папку "libs"
- Установка библиотеки SDL3 для Wasm закончена. Теперь можно использовать этот путь в своих проектах в файле CMakeListst.txt, чтобы указывать точное местоположение конфигурационных файлов библиотеки SDL3:
| Code | 1
| set(SDL3_DIR "C:/libs/SDL3-devel-3.4.0-wasm/lib/cmake/SDL3") |
|
- Примечание 1. Команда "set" создаёт переменную SDL3_DIR и устанавливает ей значение "C:/libs/SDL3-devel-3.4.0-wasm/lib/cmake/SDL3"
- Примечание 2. Имя переменной должно строго соответствовать: НазваниеБиблиотеки_DIR
Настройка проекта
- Создайте пустую папку с именем "mouse-and-touch-handlers-sdl3". Это будет папка для проекта
- Откройте созданную папку в каком-нибудь редакторе кода, например, в Notepad++ и Sublime Text4 (ST4): https://www.sublimetext.com/download Если у вас путь к ST4 находится в Path, то отройте консоль (CMD) в созданной папке "mouse-and-touch-handlers-sdl3" и введите команду:
- Примечание. Точка после subl означает - открыть текущую папку в Sublime Text 4
Пошаговое создание файла CMakeLists.txt
- В корне проекта создайте файл CMakeLists.txt
- Наберите строку указания минимальной версии CMake:
| Code | 1
| cmake_minimum_required(VERSION 3.21) |
|
- Добавьте строку, которая задаёт название проекта в системе CMake:
| Code | 1
| project(mouse-and-touch-handlers-sdl3) |
|
- Добавьте строку, которая задаёт название для генерируемых исполняемых файлов:
- Примечание. В Windows был бы сгенерирован файл app.exe, а в вебе будет app.js / app.wasm
- Добавьте строку, которая задаёт используемый стандарт языка Си:
| C | 1
| set(CMAKE_C_STANDARD 11) |
|
- Примечание 1. Для C++ эквивалентом будет строка "set(CMAKE_CXX_STANDARD 20)"
- Добавьте строку, которая указывает точное местоположение конфигурационных файлов библиотеки SDL3:
| Code | 1
| set(SDL3_DIR "C:/libs/SDL3-devel-3.4.0-wasm/lib/cmake/SDL3") |
|
- Добавьте строку, которая проверяет наличие библиотеки SDL3 в системе
| Code | 1
| find_package(SDL3 REQUIRED) |
|
- Примечание. Команда find_package() проверяет наличие библиотеки SDL3 в системе по установленному ранее пути (SDL3_DIR)
- Добавьте строку, которая привязывает библиотеку SDL3 к нашему приложению
| Code | 1
| target_link_libraries(app PRIVATE SDL3::SDL3) |
|
- Добавьте код для подключения файла исходного кода main.c:
| Code | 1
2
3
4
| target_sources(app
PRIVATE
src/main.c
) |
|
В итоге должен получиться файл:
CMakeLists.txt
| Code | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| cmake_minimum_required(VERSION 3.21)
project(mouse-and-touch-handlers-sdl3)
# Задаем название будущего приложения (в Windows это был бы app.exe, а в вебе будет app.js / app.wasm)
add_executable(app)
# Устанавливаем стандарт C
set(CMAKE_C_STANDARD 11)
# Указываем точное местоположение конфигурационных файлов библиотеки SDL3
# Имя переменной должно строго соответствовать: НазваниеБиблиотеки_DIR
set(SDL3_DIR "C:/libs/SDL3-devel-3.4.0-wasm/lib/cmake/SDL3")
# Проверяем наличие библиотеки SDL3 в системе
# Если она не будет найдены, CMake прервет настройку с ошибкой
# REQUIRED - переводится, как «обязательно» или «требуется»
find_package(SDL3 REQUIRED)
# Добавляем исходный код к проекту
target_sources(app
PRIVATE
src/main.c
) |
|
Пошаговое создание файла main.c
- Создайте папку "src", а в ней файл "main.c"
- Откройте в файл src/main.c в редакторе кода
- Примечание. Код лучше не копировать и вставлять, а набирать вручную - так лучше запоминается
- Добавьте строку для переключения на режим "Использовать SDL3-callback вместо main()"
| C | 1
| #define SDL_MAIN_USE_CALLBACKS 1 // Use the callbacks instead of main() |
|
- Добавьте строки подключения необходимых заголовочных файлов SDL3:
| C | 1
2
| #include <SDL3/SDL.h>
#include <SDL3/SDL_main.h> |
|
- Создайте две глобальные переменные: window - окно и renderer - рисовальщик:
| C | 1
2
| static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL; |
|
- Примечание. Ключевое слово static делает переменную видимой только в файле main.c
Функция SDL_AppInit() - инициализация
- Напишите функцию SDL_AppInit(), которая будет вызываться библиотекой SDL3 автоматически один раз перед запуском основного цикла приложения:
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
if (!SDL_Init(SDL_INIT_VIDEO))
{
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("Example", 400, 400, 0, &window, &renderer))
{
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetRenderVSync(renderer, 1);
return SDL_APP_CONTINUE;
} |
|
- Примечание 1. В этой функции вызывается функция SDL_Init() с аргументом SDL_INIT_VIDEO, для инициализации библиотеки SDL3. В случае, если по каким-то причинам инициализация не прошла, то выводится текст ошибки и приложение закрывается
- Примечание 2. Функция SDL_CreateWindowAndRenderer() создаёт холст размера 400 на 400 пикселей и возвращает созданные: window и renderer
- Примечание 3. Функция SDL_SetRenderVSync() включает вертикальную синхронизацию. Главный цикл приложения будет запущен с той же частотой с которой работает монитор компьютера. Это значительно снижает нагрузку на процессор. На старом ноутбуке эта разница будет примерно такая: 30% CPU с отключенной VSync и 1-3% с включенной VSync
Функция SDL_AppEvent() - обработка событий
- Напишите код функции SDL_AppEvent(), которая отвечает за события, а в данном случае за обработку события закрытия окна:
| C | 1
2
3
4
5
6
7
8
| SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_QUIT)
{
return SDL_APP_SUCCESS; // End the program, reporting success to the OS
}
return SDL_APP_CONTINUE;
} |
|
- Примечание. Эта функция отвечает за события в приложении. Например, нажатия кнопок клавиатуры, движение мыши, клик мышью, касание экрана на мобильном устройстве и т.д. В данном случае, пользователь кликает по крестику. Срабатывает функция SDL_AppEvent(). Условие if проверяет какой тип события. Если это тим SDL_EVENT_QUIT, то срабатывает тело условия if и происходит выход из программы с кодом SDL_APP_SUCCESS, то есть сообщаем операционной системе, что выход успешный. Если было какое-то другое событие, то выполнение главного цикла продолжается (return SDL_APP_CONTINUE)
Функция SDL_AppIterate() - главный цикл приложения
- Напишите код главного цикла приложения, то есть функцию SDL_AppIterate():
| C | 1
2
3
4
5
6
7
8
9
10
11
12
| SDL_AppResult SDL_AppIterate(void *appstate)
{
// Clear the screen
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
SDL_RenderClear(renderer);
// Draw here...
// Update the screen
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE;
} |
|
- Примечание 1. Функция SDL_AppIterate() вызывается библиотекой SDL3 автоматически с частотой равной частоте монитора при включённом VSync. Таким образом, если у вас монитор с частотой 60 Гц, то функция SDL_AppIterate будет вызываться 60 раз в секунд с интервалом примерно 0.016 секунды между вызовами
- Примечание 2. Функция SDL_SetRenderDrawColor задаём цвет в формате R, G, B, A (Red - красный, Green - зелёный, Blue - синий, Alpha альфа канал), где каждое значение находится в диапазоне [0, 255]. Последний параметр - это альфа канал - прозрачность: 0 - прозрачный фон, 255 - непрозрачный фон. Цвет (100, 100, 100, 255) - серый, непрозрачный
- Примечание 3. Функция SDL_RenderClear() стирает всё с холста и заливает холст заданным цветом, то есть в данном случае серый
- Примечание 4. Функция SDL_RenderPresent() - выводит кадр на экран
Функция SDL_AppQuit() - освобождение ресурсов
- Напишите код функции под названием SDL_AppQuit(), которая автоматически вызывается библиотекой SDL3 перед завершением приложения, когда пользователь закроет окно:
| C | 1
2
3
4
| void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
// SDL will clean up the window/renderer for us
} |
|
- Примечание. Эта функция нужная для освобождения ресурсов
Конфигурирование проекта, компиляция кода и запуск на локальном сервере
- В корне проекта создайте два батника со следующими названиями и содержимым:
config-web.bat
| Bash | 1
| emcmake cmake -S . -B dist -DCMAKE_BUILD_TYPE=Debug |
|
build-web.bat
| Bash | 1
2
3
4
5
6
7
8
| cd dist
cmake --build .
cd ..
mkdir public\js
set current_dir=%~dp0
copy "%current_dir%dist\app.wasm" "%current_dir%public\js"
copy "%current_dir%dist\app.js" "%current_dir%public\js" |
|
- Примечание. Батник "config-web.bat" нужно запустить один раз, а дальше во время разработки и отладки запускать батник "build-web.bat" для сборки проекта. После сборки в папке "dist" появятся два файла: "app.js" и "app.wasm", появится папка "public/js" и оба файла ("app.js" и "app.wasm") буду автоматически скопированы в папку "public/js"
- В корне проекта выполните две команды в консоли, которые запустят батники:
- Можете проверить, что появилась папка "public/js", в которой находятся два файла: "app.js" и "app.wasm"
- Создайте файл "index.html" в папке "public" со следующим содержимым:
index.html
| PHP/HTML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!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> |
|
- Примечание. Здесь создаётся элемент <canvas> c id="canvas", который будет использовать SDL3 для рисования. Подключается файл "./js/app.js". Элемент <meta name="viewport"... нужен, чтобы в нормальном масштабе отображалось на разных устройствах, например, в браузерах на смартфонах
- Запустите локальный сервер командой:
- Перейдите в браузере ПК по адресу:
- Примечание. По Wi-Fi на Android тоже работает
Обработчик клика мыши
- Добавьте заголовочный файл для использования функции printf():
- Добавьте обработчик клика мышью и выведите текст в консоль:
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
| SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_QUIT)
{
return SDL_APP_SUCCESS; // End the program, reporting success to the OS
}
else if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
printf("Клик мышью\n");
}
return SDL_APP_CONTINUE;
} |
|
- В результате получился пример:
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
| #define SDL_MAIN_USE_CALLBACKS 1 // Use the callbacks instead of main()
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
if (!SDL_Init(SDL_INIT_VIDEO))
{
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("Example", 400, 400, 0, &window, &renderer))
{
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
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; // End the program, reporting success to the OS
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
// Clear the screen
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
SDL_RenderClear(renderer);
// Draw here...
// Update the screen
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
// SDL will clean up the window/renderer for us
} |
|
- Введите команду для сборки проекта "build-web"
- Откройте новую страницу в браузере
- Откройте отладочную консоль браузера (Ctrl+Shift+J на Chrome/Edge или Ctrl+Shfit+K)
- Перейдите по адресу "localhost:8080"
[H3]Обработчик касания экрана на смартфоне[/H4]
- Добавьте обработчик касания экрана (тип события SDL_EVENT_FINGER_DOWN) в функцию SDL_AppEvent:
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_QUIT)
{
return SDL_APP_SUCCESS; // End the program, reporting success to the OS
}
else if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
printf("Клик мышью\n");
}
else if(event->type == SDL_EVENT_FINGER_DOWN)
{
printf("Касание экрана\n");
}
return SDL_APP_CONTINUE;
} |
|
- Примечание. Если вы перейдёте в отладочную консоль браузера ПК, то вторая кнопка сверху "Toggle device toolbar" переключает на эмуляцию мобильного устройства. Там вы увидите, что срабатывают оба события: SDL_EVENT_MOUSE_BUTTON_DOWN и SDL_EVENT_FINGER_DOWN. Если вам нужно событие клика на Desktop и отдельно одиночного касания экрана на Android, то можно использовать тип события SDL_EVENT_MOUSE_BUTTON_DOWN. Если нужно отдельно касания обрабатывать и отключать события мыши на Android, то можно использовать следующую опцию, которую нужно разместить до строки создания окна:
| C | 1
2
| // Отключаем генерацию событий мыши из касаний пальцем
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); |
|
Загрузка приложения на бесплатный хостинг Vercel
- Установите Node.js: https://nodejs.org/en/download
- Установите 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" }
]
}
]
} |
|
- Соберите проект в Release, а для этого замените Debug на Release в файле config-web.bat
- Сконфигурируйте проект командой:
- Собирите проект командой:
- Введите команды:
- Будет выведена ссылка на приложение в браузере: https://mouse-and-touch-handlers-sdl3.vercel.app
- Примечание. Если вы что-то изменили в приложении и пересобрали его, то достаточно ввести одну команду для перезаливки:
Вес приложения в релизе на SDL3:

Структура проекта:
|