From 0c091296a2e7b9818aeaade62d51bb9b444db24d Mon Sep 17 00:00:00 2001 From: Werner Stoop Date: Tue, 15 Oct 2024 12:20:36 +0200 Subject: [PATCH 1/3] Added emscripten support --- examples/drawing-c/Makefile | 33 +++++++++-- fenster.h | 107 +++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 7 deletions(-) diff --git a/examples/drawing-c/Makefile b/examples/drawing-c/Makefile index 6e25281..6833cc7 100644 --- a/examples/drawing-c/Makefile +++ b/examples/drawing-c/Makefile @@ -1,15 +1,36 @@ CFLAGS ?= -Wall -Wextra -std=c99 -ifeq ($(OS),Windows_NT) - LDFLAGS = -lgdi32 +ifdef EMSDK + CC = emcc + LDFLAGS += -s ASYNCIFY -s ASYNCIFY_IGNORE_INDIRECT + WEB_DIR = web + WEB = $(WEB_DIR)/index.html + OUTPUT = $(WEB) + PORT ?= 9000 else - UNAME_S := $(shell uname -s) - ifeq ($(UNAME_S),Darwin) - LDFLAGS = -framework Cocoa + ifeq ($(OS),Windows_NT) + LDFLAGS = -lgdi32 else - LDFLAGS = -lX11 + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LDFLAGS = -framework Cocoa + else + LDFLAGS = -lX11 + endif endif endif +ifdef EMSDK +$(OUTPUT): main.c ../../fenster.h | $(WEB_DIR) + $(CC) main.c -I../.. -o $@ $(CFLAGS) $(LDFLAGS) + +$(WEB_DIR): + mkdir -p $(WEB_DIR) +else main: main.c ../../fenster.h $(CC) main.c -I../.. -o $@ $(CFLAGS) $(LDFLAGS) +endif + +serve: $(WEB) + @echo Running server on port $(PORT) + @-cd $(WEB_DIR) && python3 -m http.server $(PORT) \ No newline at end of file diff --git a/fenster.h b/fenster.h index ed4332e..02c44c7 100644 --- a/fenster.h +++ b/fenster.h @@ -7,6 +7,14 @@ #include #elif defined(_WIN32) #include +#elif defined(__EMSCRIPTEN__) +#include +#include +# ifndef EM_ASYNCIFY +// EM_ASYNCIFY defaults to 1 to keep existing code working as normal +// but you have to compile with `-sASYNCIFY` +# define EM_ASYNCIFY 1 +# endif #else #define _DEFAULT_SOURCE 1 #include @@ -32,6 +40,8 @@ struct fenster { id wnd; #elif defined(_WIN32) HWND hwnd; +#elif defined(__EMSCRIPTEN__) + SDL_Surface *screen; #else Display *dpy; Window w; @@ -51,7 +61,94 @@ FENSTER_API int64_t fenster_time(void); #define fenster_pixel(f, x, y) ((f)->buf[((y) * (f)->width) + (x)]) #ifndef FENSTER_HEADER -#if defined(__APPLE__) + +#if defined(__EMSCRIPTEN__) + +FENSTER_API int fenster_open(struct fenster *f) { + SDL_Init(SDL_INIT_VIDEO); + f->screen = SDL_SetVideoMode(f->width, f->height, 32, SDL_SWSURFACE); + emscripten_set_window_title(f->title); + return 0; +} + +// https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlkey.html +// clang-format off +static const int FENSTER_KEYCODES[] = {SDLK_BACKSPACE,8,SDLK_DELETE,127,SDLK_DOWN,18,SDLK_END,5,SDLK_ESCAPE,27,SDLK_HOME,2,SDLK_INSERT,26,SDLK_LEFT,20,SDLK_PAGEDOWN,4,SDLK_PAGEUP,3,SDLK_RETURN,10,SDLK_RIGHT,19,SDLK_TAB,9,SDLK_UP,17,SDLK_QUOTE,39,SDLK_BACKSLASH,92,SDLK_LEFTBRACKET,91,SDLK_RIGHTBRACKET,93,SDLK_COMMA,44,SDLK_EQUALS,61,SDLK_BACKQUOTE,96,SDLK_MINUS,45,SDLK_PERIOD,46,SDLK_SEMICOLON,59,SDLK_SLASH,47,SDLK_SPACE,32,SDLK_a,65,SDLK_b,66,SDLK_c,67,SDLK_d,68,SDLK_e,69,SDLK_f,70,SDLK_g,71,SDLK_h,72,SDLK_i,73,SDLK_j,74,SDLK_k,75,SDLK_l,76,SDLK_m,77,SDLK_n,78,SDLK_o,79,SDLK_p,80,SDLK_q,81,SDLK_r,82,SDLK_s,83,SDLK_t,84,SDLK_u,85,SDLK_v,86,SDLK_w,87,SDLK_x,88,SDLK_y,89,SDLK_z,90,SDLK_0,48,SDLK_1,49,SDLK_2,50,SDLK_3,51,SDLK_4,52,SDLK_5,53,SDLK_6,54,SDLK_7,55,SDLK_8,56,SDLK_9,57}; +// clang-format on + +FENSTER_API int fenster_loop(struct fenster *f) { + + // Unfortunately the screen is ABGR while the fenster buffer is ARGB + if (SDL_MUSTLOCK(f->screen)) SDL_LockSurface(f->screen); + for (int i = 0; i < f->width * f->height; i++) { + uint32_t pixel = f->buf[i]; +#if 0 + uint32_t r = (pixel >> 16) & 0xFF; + uint32_t g = (pixel >> 8) & 0xFF; + uint32_t b = (pixel >> 0) & 0xFF; + *((Uint32*)f->screen->pixels + i) = SDL_MapRGBA(f->screen->format, r, g, b, 0); +#else + pixel = (pixel & 0xFF00FF00) | ((pixel & 0xFF0000) >> 16) | ((pixel & 0xFF) << 16); + *((Uint32*)f->screen->pixels + i) = pixel; +#endif + } + if (SDL_MUSTLOCK(f->screen)) SDL_UnlockSurface(f->screen); + + SDL_Flip(f->screen); + + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_MOUSEMOTION: { + f->x = event.motion.x; + f->y = event.motion.y; + } break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: { + f->x = event.button.x; + f->y = event.button.y; + if(event.button.button == SDL_BUTTON_LEFT) + f->mouse = event.type == SDL_MOUSEBUTTONDOWN; + } break; + case SDL_KEYDOWN: + case SDL_KEYUP: { + // event.key.keysym + int mask = 0; + switch(event.key.keysym.sym) { + case SDLK_RSHIFT: + case SDLK_LSHIFT: mask = 0x2; break; + case SDLK_RCTRL: + case SDLK_LCTRL: mask = 0x1; break; + case SDLK_RALT: + case SDLK_LALT: mask = 0x4; break; + case SDLK_RSUPER: + case SDLK_LSUPER: mask = 0x8; break; + } + if(mask) { + f->mod = (event.key.state == SDL_PRESSED) ? f->mod | mask : f->mod & ~mask; + break; + } + + for (unsigned int i = 0; i < (sizeof FENSTER_KEYCODES / sizeof FENSTER_KEYCODES[0]); i += 2) { + if (FENSTER_KEYCODES[i] == event.key.keysym.sym) { + f->keys[FENSTER_KEYCODES[i + 1]] = (event.key.state == SDL_PRESSED); + break; + } + } + } break; + default: break; + } + } + + return 0; +} + +FENSTER_API void fenster_close(struct fenster *f) { + (void)f; + SDL_Quit(); +} + +#elif defined(__APPLE__) #define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s)) #define msg1(r, o, s, A, a) \ ((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a) @@ -318,6 +415,14 @@ FENSTER_API int64_t fenster_time() { QueryPerformanceCounter(&count); return (int64_t)(count.QuadPart * 1000.0 / freq.QuadPart); } +#elif defined(__EMSCRIPTEN__) +FENSTER_API void fenster_sleep(int64_t ms) { + SDL_Delay((uint32_t)ms); +} + +FENSTER_API int64_t fenster_time(void) { + return (int64_t)SDL_GetTicks(); +} #else FENSTER_API void fenster_sleep(int64_t ms) { struct timespec ts; From 0b07bc8638940bbd8870b61edbdfba5dfb6a64b5 Mon Sep 17 00:00:00 2001 From: Werner Stoop Date: Tue, 15 Oct 2024 15:14:28 +0200 Subject: [PATCH 2/3] Attempt to build the doom example as a web application (but it's still borked) --- examples/doom-c/Makefile.fenster | 38 +++++++++++++++++++++------ examples/doom-c/doomgeneric_fenster.c | 2 ++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/examples/doom-c/Makefile.fenster b/examples/doom-c/Makefile.fenster index 995c045..547ef79 100644 --- a/examples/doom-c/Makefile.fenster +++ b/examples/doom-c/Makefile.fenster @@ -1,18 +1,28 @@ CFLAGS+=-Wall -DNORMALUNIX -DLINUX -DSNDSERV -D_DEFAULT_SOURCE -LIBS+=-lm -lc -ifeq ($(OS),Windows_NT) - LIBS+= -lgdi32 +LIBS+=-lm + +ifdef EMSDK + CC = emcc + LDFLAGS += -s ASYNCIFY -s WASM=0 + WEB_DIR = web + WEB = $(WEB_DIR)/index.html + OUTPUT = $(WEB) + PORT ?= 9000 else - UNAME_S := $(shell uname -s) - ifeq ($(UNAME_S),Darwin) - LIBS+=-framework Cocoa + ifeq ($(OS),Windows_NT) + LIBS+= -lgdi32 else - LIBS+=-lX11 + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS+=-framework Cocoa + else + LIBS+=-lX11 + endif endif + OUTPUT=doomgeneric endif OBJDIR=build -OUTPUT=doomgeneric SRC_DOOM = i_main.o dummy.o am_map.o doomdef.o doomstat.o dstrings.o d_event.o d_items.o d_iwad.o d_loop.o d_main.o d_mode.o d_net.o f_finale.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o i_cdmus.o i_endoom.o i_joystick.o i_scale.o i_sound.o i_system.o i_timer.o memio.o m_argv.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o m_menu.o m_misc.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o sha1.o sounds.o statdump.o st_lib.o st_stuff.o s_sound.o tables.o v_video.o wi_stuff.o w_checksum.o w_file.o w_main.o w_wad.o z_zone.o w_file_stdc.o i_input.o i_video.o doomgeneric.o doomgeneric_fenster.o OBJS += $(addprefix $(OBJDIR)/, $(SRC_DOOM)) @@ -23,8 +33,17 @@ clean: rm -rf $(OBJDIR) rm -f $(OUTPUT) +ifdef EMSDK +$(OUTPUT): $(OBJS) | $(WEB_DIR) + @echo Creating $@... + $(CC) $^ -o $@ $(LDFLAGS) --preload-file doom2.wad + +$(WEB_DIR): + mkdir -p $(WEB_DIR) +else $(OUTPUT): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(OUTPUT) $(LIBS) +endif $(OBJS): | $(OBJDIR) @@ -37,3 +56,6 @@ $(OBJDIR)/%.o: %.c x: @echo $(SRC_DOOM) +serve: $(WEB) + @echo Running server on port $(PORT) + @-cd $(WEB_DIR) && python3 -m http.server $(PORT) diff --git a/examples/doom-c/doomgeneric_fenster.c b/examples/doom-c/doomgeneric_fenster.c index cb8d4c4..36e28bf 100644 --- a/examples/doom-c/doomgeneric_fenster.c +++ b/examples/doom-c/doomgeneric_fenster.c @@ -1,3 +1,5 @@ +#include + #include "../../fenster.h" #include "doomgeneric.h" #include "doomkeys.h" From ae026e67f9ca0b9375767151db2f08ee8a69babb Mon Sep 17 00:00:00 2001 From: Werner Stoop Date: Wed, 30 Oct 2024 15:29:06 +0200 Subject: [PATCH 3/3] Fixed fenster_audio.h for Windows --- examples/sound-c/Makefile | 2 +- fenster_audio.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/sound-c/Makefile b/examples/sound-c/Makefile index 1abfc37..624fea3 100644 --- a/examples/sound-c/Makefile +++ b/examples/sound-c/Makefile @@ -1,7 +1,7 @@ CFLAGS ?= -Wall -Wextra -std=c99 ifeq ($(OS),Windows_NT) - LDFLAGS = -lgdi32 + LDFLAGS = -lgdi32 -lwinmm else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) diff --git a/fenster_audio.h b/fenster_audio.h index 52ef12e..54317cd 100644 --- a/fenster_audio.h +++ b/fenster_audio.h @@ -95,10 +95,10 @@ FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf, } #elif defined(_WIN32) FENSTER_API int fenster_audio_open(struct fenster_audio *fa) { - WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, FENSTER_SAMPLE_RATE, FENSTER_SAMPLE_RATE * 2, 1, 16, 0}; + WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, FENSTER_SAMPLE_RATE, FENSTER_SAMPLE_RATE * 2, 2, 16, 0}; waveOutOpen(&fa->wo, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL); for (int i = 0; i < 2; i++) { - fa->hdr[i].lpData = fa->buf[i]; + fa->hdr[i].lpData = (LPSTR)fa->buf[i]; fa->hdr[i].dwBufferLength = FENSTER_AUDIO_BUFSZ * 2; waveOutPrepareHeader(fa->wo, &fa->hdr[i], sizeof(WAVEHDR)); waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));