Skip to content

Commit 2306e68

Browse files
authored
Add initial / experimental port of SDL3 (emscripten-core#23630)
I just got a couple of test working, but everything seems good so far. Fixes: emscripten-core#23608
1 parent 5024d16 commit 2306e68

File tree

9 files changed

+898
-85
lines changed

9 files changed

+898
-85
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.4 (in development)
2222
----------------------
23+
- An initial port of SDL3 was added. Use it with `-sUSE_SDL=3`. This port
24+
is still experimental. (#23630)
2325
- The `--output_eol` command line flag was renamed `--output-eol` for
2426
consistency with other flags. The old name continues to work as an alias.
2527
(#20735)

test/browser/test_sdl2_canvas_write.c

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,75 @@
55

66
#include <assert.h>
77
#include <SDL.h>
8-
#include <emscripten.h>
8+
#include <emscripten/em_asm.h>
99

10-
static void sdlError(const char *str)
11-
{
12-
fprintf(stderr, "Error at %s: %s\n", str, SDL_GetError());
13-
emscripten_force_exit(1);
10+
static void sdlError(const char* str) {
11+
fprintf(stderr, "Error at %s: %s\n", str, SDL_GetError());
12+
exit(1);
1413
}
1514

16-
void draw(SDL_Window *window, SDL_Surface *surface) {
17-
int x, y;
15+
void draw(SDL_Window* window, SDL_Surface* surface) {
16+
int x, y;
1817

19-
if (SDL_MUSTLOCK(surface)) {
20-
if (SDL_LockSurface(surface) != 0) sdlError("SDL_LockSurface");
21-
}
18+
if (SDL_MUSTLOCK(surface)) {
19+
if (SDL_LockSurface(surface) != 0)
20+
sdlError("SDL_LockSurface");
21+
}
2222

23-
for (y = 0; y < 256; y++) {
24-
Uint32 *p = (Uint32 *)(((Uint8 *)surface->pixels) +
25-
surface->pitch * y);
26-
for (x = 0; x < 256; x++) {
27-
*(p++) = SDL_MapRGB(surface->format, x, x ^ y, y);
28-
}
23+
for (y = 0; y < 256; y++) {
24+
Uint32* p = (Uint32*)(((Uint8*)surface->pixels) + surface->pitch * y);
25+
for (x = 0; x < 256; x++) {
26+
*(p++) = SDL_MapRGB(surface->format, x, x ^ y, y);
2927
}
28+
}
3029

31-
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
32-
if (SDL_UpdateWindowSurface(window) != 0)
33-
sdlError("SDL_UpdateWindowSurface");
30+
if (SDL_MUSTLOCK(surface))
31+
SDL_UnlockSurface(surface);
32+
if (SDL_UpdateWindowSurface(window) != 0)
33+
sdlError("SDL_UpdateWindowSurface");
3434
}
3535

3636
void verify(void) {
37-
int res = EM_ASM_INT({
38-
var ctx = Module['canvas'].getContext('2d');
39-
var data = ctx.getImageData(0, 0, 256, 256).data;
40-
var idx = 0;
41-
for (var y = 0; y < 256; y++) {
42-
for (var x = 0; x < 256; x++) {
43-
if (data[idx ] !== x ||
44-
data[idx + 1] !== (x ^ y) ||
45-
data[idx + 2] !== y ||
46-
data[idx + 3] !== 255) {
47-
return 1;
48-
}
49-
idx += 4;
37+
int res = EM_ASM_INT({
38+
var ctx = Module['canvas'].getContext('2d');
39+
var data = ctx.getImageData(0, 0, 256, 256).data;
40+
var idx = 0;
41+
for (var y = 0; y < 256; y++) {
42+
for (var x = 0; x < 256; x++) {
43+
if (data[idx ] !== x ||
44+
data[idx + 1] !== (x ^ y) ||
45+
data[idx + 2] !== y ||
46+
data[idx + 3] !== 255) {
47+
return 1;
5048
}
49+
idx += 4;
5150
}
52-
return 0;
53-
});
51+
}
52+
return 0;
53+
});
5454

55-
printf("%s\n", res ? "FAIL" : "PASS");
56-
assert(res == 0);
55+
printf("%s\n", res ? "FAIL" : "PASS");
56+
assert(res == 0);
5757
}
5858

5959
int main(void) {
60-
SDL_Window *window;
61-
SDL_Surface *surface;
60+
SDL_Window* window;
61+
SDL_Surface* surface;
6262

63-
if (SDL_Init(SDL_INIT_VIDEO) != 0) sdlError("SDL_Init");
63+
if (SDL_Init(SDL_INIT_VIDEO) != 0)
64+
sdlError("SDL_Init");
6465

65-
window = SDL_CreateWindow("SDL 2 test",
66-
0, 0,
67-
256, 256,
68-
SDL_WINDOW_SHOWN);
69-
if (window == NULL) sdlError("SDL_CreateWindow");
70-
surface = SDL_GetWindowSurface(window);
71-
if (surface == NULL) sdlError("SDL_GetWindowSurface");
66+
window = SDL_CreateWindow("SDL 2 test", 0, 0, 256, 256, SDL_WINDOW_SHOWN);
67+
if (window == NULL) {
68+
sdlError("SDL_CreateWindow");
69+
}
70+
surface = SDL_GetWindowSurface(window);
71+
if (surface == NULL) {
72+
sdlError("SDL_GetWindowSurface");
73+
}
7274

73-
draw(window, surface);
75+
draw(window, surface);
7476

75-
verify();
76-
return 0;
77+
verify();
78+
return 0;
7779
}

test/browser/test_sdl2_misc.c

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,40 @@
1313

1414
#include <emscripten.h>
1515

16-
int main(int argc, char *argv[])
17-
{
18-
SDL_Window *window;
19-
20-
if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
21-
printf("Unable to initialize SDL: %s\n", SDL_GetError());
22-
return 1;
23-
}
24-
25-
window = SDL_CreateWindow(
26-
"sdl2_misc",
27-
SDL_WINDOWPOS_UNDEFINED,
28-
SDL_WINDOWPOS_UNDEFINED,
29-
100,
30-
100,
31-
0
32-
);
33-
34-
EM_ASM({
35-
assert(document.title === 'sdl2_misc');
36-
});
37-
const char* intended = "a custom window title";
38-
SDL_SetWindowTitle(window, intended);
39-
const char* seen = SDL_GetWindowTitle(window);
40-
if (strcmp(intended, seen) != 0) {
41-
printf("Got a weird title back: %s\n", seen);
42-
return 1;
43-
}
44-
EM_ASM({
45-
assert(document.title === 'a custom window title');
46-
});
47-
48-
SDL_DestroyWindow(window);
49-
SDL_Quit();
50-
51-
return 0;
16+
int main(int argc, char *argv[]) {
17+
SDL_Window *window;
18+
19+
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
20+
printf("Unable to initialize SDL: %s\n", SDL_GetError());
21+
return 1;
22+
}
23+
24+
window = SDL_CreateWindow(
25+
"sdl2_misc",
26+
SDL_WINDOWPOS_UNDEFINED,
27+
SDL_WINDOWPOS_UNDEFINED,
28+
100,
29+
100,
30+
0
31+
);
32+
33+
EM_ASM({
34+
assert(document.title === 'sdl2_misc');
35+
});
36+
const char* intended = "a custom window title";
37+
SDL_SetWindowTitle(window, intended);
38+
const char* seen = SDL_GetWindowTitle(window);
39+
if (strcmp(intended, seen) != 0) {
40+
printf("Got a weird title back: %s\n", seen);
41+
return 1;
42+
}
43+
44+
EM_ASM({
45+
assert(document.title === 'a custom window title');
46+
});
47+
48+
SDL_DestroyWindow(window);
49+
SDL_Quit();
50+
51+
return 0;
5252
}

test/browser/test_sdl3_canvas_write.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <assert.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <SDL3/SDL.h>
10+
#include <emscripten/em_asm.h>
11+
12+
static void sdlError(const char* str) {
13+
fprintf(stderr, "Error at %s: %s\n", str, SDL_GetError());
14+
exit(1);
15+
}
16+
17+
void draw(SDL_Window* window, SDL_Surface* surface) {
18+
int x, y;
19+
20+
if (SDL_MUSTLOCK(surface)) {
21+
if (!SDL_LockSurface(surface)) {
22+
sdlError("SDL_LockSurface");
23+
}
24+
}
25+
26+
for (y = 0; y < 256; y++) {
27+
Uint32* p = (Uint32*)(((Uint8*)surface->pixels) + surface->pitch * y);
28+
for (x = 0; x < 256; x++) {
29+
*(p++) = SDL_MapSurfaceRGB(surface, x, x ^ y, y);
30+
}
31+
}
32+
33+
if (SDL_MUSTLOCK(surface)) {
34+
SDL_UnlockSurface(surface);
35+
}
36+
if (!SDL_UpdateWindowSurface(window)) {
37+
sdlError("SDL_UpdateWindowSurface");
38+
}
39+
}
40+
41+
void verify(void) {
42+
int res = EM_ASM_INT({
43+
var ctx = Module['canvas'].getContext('2d');
44+
var data = ctx.getImageData(0, 0, 256, 256).data;
45+
var idx = 0;
46+
for (var y = 0; y < 256; y++) {
47+
for (var x = 0; x < 256; x++) {
48+
if (data[idx ] !== x ||
49+
data[idx + 1] !== (x ^ y) ||
50+
data[idx + 2] !== y ||
51+
data[idx + 3] !== 255) {
52+
return 1;
53+
}
54+
idx += 4;
55+
}
56+
}
57+
return 0;
58+
});
59+
60+
printf("%s\n", res ? "FAIL" : "PASS");
61+
assert(res == 0);
62+
}
63+
64+
int main(void) {
65+
SDL_Window* window;
66+
SDL_Surface* surface;
67+
68+
if (!SDL_Init(SDL_INIT_VIDEO)) {
69+
sdlError("SDL_Init");
70+
}
71+
72+
window = SDL_CreateWindow("SDL 2 test", 256, 256, 0);
73+
if (window == NULL) {
74+
sdlError("SDL_CreateWindow");
75+
}
76+
surface = SDL_GetWindowSurface(window);
77+
if (surface == NULL) {
78+
sdlError("SDL_GetWindowSurface");
79+
}
80+
81+
draw(window, surface);
82+
83+
verify();
84+
return 0;
85+
}

test/browser/test_sdl3_misc.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2025 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include "SDL3/SDL.h"
9+
10+
#include <assert.h>
11+
#include <stdio.h>
12+
#include <string.h>
13+
#include <assert.h>
14+
15+
#include <emscripten.h>
16+
17+
int main(int argc, char* argv[]) {
18+
SDL_Window* window;
19+
20+
if (!SDL_Init(SDL_INIT_VIDEO)) {
21+
printf("Unable to initialize SDL: %s\n", SDL_GetError());
22+
assert(false);
23+
}
24+
25+
window = SDL_CreateWindow("sdl3_misc", 100, 100, 0);
26+
27+
EM_ASM({
28+
assert(document.title == 'sdl3_misc');
29+
});
30+
const char* intended = "a custom window title";
31+
SDL_SetWindowTitle(window, intended);
32+
const char* seen = SDL_GetWindowTitle(window);
33+
if (strcmp(intended, seen) != 0) {
34+
printf("Got a weird title back: %s\n", seen);
35+
assert(false);
36+
}
37+
EM_ASM({
38+
assert(document.title == 'a custom window title');
39+
});
40+
41+
SDL_DestroyWindow(window);
42+
SDL_Quit();
43+
44+
return 0;
45+
}

test/test_browser.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3261,6 +3261,14 @@ def test_sdl2_mixer_music(self, formats, flags, music_name):
32613261
args += ['-lc++', '-lc++abi']
32623262
self.btest_exit('test_sdl2_mixer_music.c', args=args)
32633263

3264+
def test_sdl3_misc(self):
3265+
self.emcc_args.append('-Wno-experimental')
3266+
self.btest_exit('test_sdl3_misc.c', args=['-sUSE_SDL=3'])
3267+
3268+
def test_sdl3_canvas_write(self):
3269+
self.emcc_args.append('-Wno-experimental')
3270+
self.btest_exit('test_sdl3_canvas_write.c', args=['-sUSE_SDL=3'])
3271+
32643272
@requires_graphics_hardware
32653273
@no_wasm64('cocos2d ports does not compile with wasm64')
32663274
def test_cocos2d_hello(self):

test/test_other.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,6 +2471,13 @@ def test_sdl2_linkable(self):
24712471
self.emcc(test_file('browser/test_sdl2_misc.c'), ['-sLINKABLE', '-sUSE_SDL=2'], output_filename='a.out.js')
24722472
self.emcc(test_file('browser/test_sdl2_misc.c'), ['-sLINKABLE', '--use-port=sdl2'], output_filename='a.out.js')
24732473

2474+
def test_sdl3_linkable(self):
2475+
# Ensure that SDL2 can be built with LINKABLE. This implies there are no undefined
2476+
# symbols in the library (because LINKABLE includes the entire library).
2477+
self.emcc_args.append('-Wno-experimental')
2478+
self.emcc(test_file('browser/test_sdl3_misc.c'), ['-sLINKABLE', '-sUSE_SDL=3'], output_filename='a.out.js')
2479+
self.emcc(test_file('browser/test_sdl3_misc.c'), ['-sLINKABLE', '--use-port=sdl3'], output_filename='a.out.js')
2480+
24742481
@requires_network
24752482
def test_sdl2_gfx_linkable(self):
24762483
# Same as above but for sdl2_gfx library

0 commit comments

Comments
 (0)