diff --git a/src/compat/SDL/SDL.h b/src/compat/SDL/SDL.h new file mode 100644 index 0000000..fb1fa2b --- /dev/null +++ b/src/compat/SDL/SDL.h @@ -0,0 +1,3 @@ +/* SDL/SDL.h for the fredygames.com browser build of Zatacka X: the SDL 2 header, same API. + * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like Zatacka X. */ +#include diff --git a/src/compat/SDL/SDL_image.h b/src/compat/SDL/SDL_image.h new file mode 100644 index 0000000..aa696ff --- /dev/null +++ b/src/compat/SDL/SDL_image.h @@ -0,0 +1,3 @@ +/* SDL/SDL_image.h for the fredygames.com browser build of Zatacka X: the SDL 2 header, same API. + * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like Zatacka X. */ +#include diff --git a/src/compat/SDL/SDL_mixer.h b/src/compat/SDL/SDL_mixer.h new file mode 100644 index 0000000..565ec23 --- /dev/null +++ b/src/compat/SDL/SDL_mixer.h @@ -0,0 +1,3 @@ +/* SDL/SDL_mixer.h for the fredygames.com browser build of Zatacka X: the SDL 2 header, same API. + * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like Zatacka X. */ +#include diff --git a/src/compat/SDL/SDL_stdinc.h b/src/compat/SDL/SDL_stdinc.h new file mode 100644 index 0000000..fa52ade --- /dev/null +++ b/src/compat/SDL/SDL_stdinc.h @@ -0,0 +1,3 @@ +/* SDL/SDL_stdinc.h for the fredygames.com browser build of Zatacka X: the SDL 2 header, same API. + * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like Zatacka X. */ +#include diff --git a/src/compat/SDL/SDL_ttf.h b/src/compat/SDL/SDL_ttf.h new file mode 100644 index 0000000..bbd8395 --- /dev/null +++ b/src/compat/SDL/SDL_ttf.h @@ -0,0 +1,3 @@ +/* SDL/SDL_ttf.h for the fredygames.com browser build of Zatacka X: the SDL 2 header, same API. + * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like Zatacka X. */ +#include diff --git a/src/zatackax.c b/src/zatackax.c index dcc9393..62f7386 100755 --- a/src/zatackax.c +++ b/src/zatackax.c @@ -2066,6 +2066,14 @@ void exitGame(int status) exit(status); } +#ifdef __EMSCRIPTEN__ +/* Called by the page when the tab is hidden or closed. */ +void fredygames_save(void) +{ + saveSettings(); +} +#endif + int main(void) { static Uint32 prevtime = 0; @@ -2198,17 +2206,7 @@ int main(void) else if (event.type == SDL_QUIT) { exitGame(0); } - else if (event.type == SDL_VIDEORESIZE) { - WINDOW_W = event.resize.w; - WINDOW_H = event.resize.h; - if (!initScreen()) { - return 1; - } - - initHitMap(WINDOW_W, WINDOW_H); - refreshGameScreen(); - } } if (!screenFreeze) { Uint32 timenow = SDL_GetTicks(); @@ -2218,11 +2216,33 @@ int main(void) if (delta < 1000/FPS_CAP) { SDL_Delay(1000/FPS_CAP - delta); } +#ifdef __EMSCRIPTEN__ + else { + /* Late frame: still gives the browser a turn to show it. */ + SDL_Delay(1); + } +#endif if (curScene->logicFunc()) { curScene->displayFunc(); } } +#ifdef __EMSCRIPTEN__ + else { + /* Frozen end-of-round screen: wait for a key without hogging the page. */ + SDL_Delay(10); + } + + /* Browser build: keeps the settings in the browser every 30 seconds. */ + { + static Uint32 lastSave = 0; + Uint32 now = SDL_GetTicks(); + if (now - lastSave > 30000) { + lastSave = now; + saveSettings(); + } + } +#endif } return 0; diff --git a/src/ztksdl12.c b/src/ztksdl12.c new file mode 100644 index 0000000..001d4fe --- /dev/null +++ b/src/ztksdl12.c @@ -0,0 +1,168 @@ +/* + * ztksdl12.c: see ztksdl12.h. The game draws on the surface of SDL_SetVideoMode (here a 32-bit surface in + * memory, as the game asks for); each SDL_Flip uploads it to a streaming texture and shows it. + * Copyright (C) 2026 FredyGames. Distributed under the GNU GPL version 3 or later, like the game. + */ +#include "ztksdl12.h" + +#undef SDL_SetVideoMode +#undef SDL_FreeSurface +#undef SDL_SetColorKey +#undef SDL_PollEvent +#undef SDL_Delay +#undef SDLK_UNKNOWN +#undef SDLK_BACKSPACE +#undef SDLK_TAB +#undef SDLK_CLEAR +#undef SDLK_RETURN +#undef SDLK_PAUSE +#undef SDLK_ESCAPE +#undef SDLK_DELETE +#undef SDLK_UP +#undef SDLK_DOWN +#undef SDLK_RIGHT +#undef SDLK_LEFT + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Texture *texture = NULL; +static SDL_Surface *screen = NULL; + +SDL_Surface *ztk_SetVideoMode(int width, int height, int bpp, Uint32 flags) +{ + (void)bpp; (void)flags; + if (window == NULL) { + window = SDL_CreateWindow("Zatacka X", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0); + if (window == NULL) + return NULL; + renderer = SDL_CreateRenderer(window, -1, 0); + if (renderer == NULL) + return NULL; + } + if (texture != NULL) + SDL_DestroyTexture(texture); + if (screen != NULL) + SDL_FreeSurface(screen); + SDL_SetWindowSize(window, width, height); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, width, height); + screen = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGB888); + return screen; +} + +int ztk_Flip(SDL_Surface *surface) +{ + if (surface != screen || texture == NULL) + return -1; + SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + return 0; +} + +/* SDL 1.2 let the program free the display surface (the next SDL_SetVideoMode makes a new one): here the + surface belongs to the shim, so freeing it does nothing. */ +void ztk_FreeSurface(SDL_Surface *surface) +{ + if (surface != NULL && surface != screen) + SDL_FreeSurface(surface); +} + +SDL_Surface *ztk_DisplayFormatAlpha(SDL_Surface *surface) +{ + return SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); +} + +/* SDL 1.2 per-surface alpha: SDL_SRCALPHA turns blending on with that alpha, no flag turns it off. */ +int ztk_SetAlpha(SDL_Surface *surface, Uint32 flags, Uint8 alpha) +{ + if (flags & SDL_SRCALPHA) { + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); + return SDL_SetSurfaceAlphaMod(surface, alpha); + } + SDL_SetSurfaceBlendMode(surface, surface->format->Amask ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE); + return SDL_SetSurfaceAlphaMod(surface, 255); +} + +int ztk_SetColorKey(SDL_Surface *surface, Uint32 flags, Uint32 key) +{ + return SDL_SetColorKey(surface, (flags & SDL_SRCCOLORKEY) ? SDL_TRUE : SDL_FALSE, key); +} + +/* SDL 2 key code -> SDL 1.2 key symbol (0 when there is none). The SDL 2 codes of keys that are not + characters are the scancode with bit 30 set. */ +static int key12(Sint32 k) +{ + if (k >= 0 && k < 128) + return k; + if (k & 0x40000000) { + int sc = k & 0xFFFF; + if (sc >= 58 && sc <= 69) /* F1-F12 */ + return 282 + (sc - 58); + if (sc >= 104 && sc <= 106) /* F13-F15 */ + return 294 + (sc - 104); + if (sc >= 89 && sc <= 97) /* keypad 1-9 */ + return 257 + (sc - 89); + switch (sc) { + case 98: return 256; /* keypad 0 */ + case 99: return 266; /* keypad period */ + case 84: return 267; /* keypad divide */ + case 85: return 268; /* keypad multiply */ + case 86: return 269; /* keypad minus */ + case 87: return 270; /* keypad plus */ + case 88: return 271; /* keypad enter */ + case 103: return 272; /* keypad equals */ + case 82: return 273; /* up */ + case 81: return 274; /* down */ + case 79: return 275; /* right */ + case 80: return 276; /* left */ + case 73: return 277; /* insert */ + case 74: return 278; /* home */ + case 77: return 279; /* end */ + case 75: return 280; /* page up */ + case 78: return 281; /* page down */ + case 83: return 300; /* num lock */ + case 57: return 301; /* caps lock */ + case 71: return 302; /* scroll lock */ + case 229: return 303; /* right shift */ + case 225: return 304; /* left shift */ + case 228: return 305; /* right ctrl */ + case 224: return 306; /* left ctrl */ + case 230: return 307; /* right alt */ + case 226: return 308; /* left alt */ + case 231: return 312; /* right super */ + case 227: return 311; /* left super */ + case 72: return 19; /* pause */ + case 70: return 316; /* print screen */ + case 118: return 319; /* menu */ + } + } + return 0; +} + +/* SDL 1.2 had no key repeat unless asked for; SDL 2 always has it. Repeats are dropped here, and the key + symbols of the events are the SDL 1.2 ones. */ +int ztk_PollEvent(SDL_Event *event) +{ + while (SDL_PollEvent(event)) { + if (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) { + if (event->type == SDL_KEYDOWN && event->key.repeat) + continue; + event->key.keysym.sym = key12(event->key.keysym.sym); + } + return 1; + } + return 0; +} + +void ztk_WM_SetCaption(const char *title, const char *icon) +{ + (void)icon; + if (window != NULL && title != NULL) + SDL_SetWindowTitle(window, title); +} + +void ztk_WM_SetIcon(SDL_Surface *icon, Uint8 *mask) +{ + (void)icon; (void)mask; +} diff --git a/src/ztksdl12.h b/src/ztksdl12.h new file mode 100644 index 0000000..2429063 --- /dev/null +++ b/src/ztksdl12.h @@ -0,0 +1,132 @@ +/* + * ztksdl12.h: the part of the SDL 1.2 API that Zatacka X uses, on top of SDL 2, for the fredygames.com + * browser build (Emscripten). Included before every source file (-include). + * Written for that build. + * Copyright (C) 2026 FredyGames. Distributed under the GNU GPL version 3 or later, like the game. + */ +#ifndef ZTKSDL12_H +#define ZTKSDL12_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int SDLKey; + +#define SDL_SWSURFACE 0x00000000 +#define SDL_HWSURFACE 0x00000001 +#define SDL_RESIZABLE 0x00000010 +#define SDL_FULLSCREEN 0x80000000 +#define SDL_SRCCOLORKEY 0x00001000 +#define SDL_SRCALPHA 0x00010000 +/* SDL 2 has no resize event of this kind: the page keeps the window at one size. */ +#define SDL_VIDEORESIZE 0x7ffe +#undef SDL_INIT_EVERYTHING +#define SDL_INIT_EVERYTHING (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER) + +SDL_Surface *ztk_SetVideoMode(int width, int height, int bpp, Uint32 flags); +int ztk_Flip(SDL_Surface *screen); +void ztk_FreeSurface(SDL_Surface *surface); +SDL_Surface *ztk_DisplayFormatAlpha(SDL_Surface *surface); +int ztk_SetAlpha(SDL_Surface *surface, Uint32 flags, Uint8 alpha); +int ztk_SetColorKey(SDL_Surface *surface, Uint32 flags, Uint32 key); +int ztk_PollEvent(SDL_Event *event); +void ztk_WM_SetCaption(const char *title, const char *icon); +void ztk_WM_SetIcon(SDL_Surface *icon, Uint8 *mask); + +#ifdef __cplusplus +} +#endif + +#define SDL_SetVideoMode ztk_SetVideoMode +#define SDL_Flip ztk_Flip +#undef SDL_FreeSurface +#define SDL_FreeSurface ztk_FreeSurface +#define SDL_DisplayFormatAlpha ztk_DisplayFormatAlpha +#define SDL_SetAlpha ztk_SetAlpha +#define SDL_SetColorKey ztk_SetColorKey +#define SDL_PollEvent ztk_PollEvent +#define SDL_WM_SetCaption ztk_WM_SetCaption +#define SDL_WM_SetIcon ztk_WM_SetIcon +/* The loop waits for the next frame with SDL_Delay: here that hands the page back to the browser (Asyncify). */ +#undef SDL_Delay +#define SDL_Delay(ms) emscripten_sleep(ms) + +/* The game indexes arrays with SDL 1.2 key symbols (322 of them): the key names have the SDL 1.2 values, and + ztk_PollEvent converts the SDL 2 key codes of each event to them. */ +#undef SDLK_UNKNOWN +#define SDLK_UNKNOWN 0 +#undef SDLK_BACKSPACE +#define SDLK_BACKSPACE 8 +#undef SDLK_TAB +#define SDLK_TAB 9 +#undef SDLK_CLEAR +#define SDLK_CLEAR 12 +#undef SDLK_RETURN +#define SDLK_RETURN 13 +#undef SDLK_PAUSE +#define SDLK_PAUSE 19 +#undef SDLK_ESCAPE +#define SDLK_ESCAPE 27 +#undef SDLK_DELETE +#define SDLK_DELETE 127 +#undef SDLK_UP +#define SDLK_UP 273 +#undef SDLK_DOWN +#define SDLK_DOWN 274 +#undef SDLK_RIGHT +#define SDLK_RIGHT 275 +#undef SDLK_LEFT +#define SDLK_LEFT 276 +#undef SDLK_INSERT +#define SDLK_INSERT 277 +#undef SDLK_HOME +#define SDLK_HOME 278 +#undef SDLK_END +#define SDLK_END 279 +#undef SDLK_PAGEUP +#define SDLK_PAGEUP 280 +#undef SDLK_PAGEDOWN +#define SDLK_PAGEDOWN 281 +#undef SDLK_F1 +#define SDLK_F1 282 +#undef SDLK_F2 +#define SDLK_F2 283 +#undef SDLK_F3 +#define SDLK_F3 284 +#undef SDLK_F15 +#define SDLK_F15 296 +#undef SDLK_SCROLLOCK +#define SDLK_SCROLLOCK 302 +#undef SDLK_RSHIFT +#define SDLK_RSHIFT 303 +#undef SDLK_LSHIFT +#define SDLK_LSHIFT 304 +#undef SDLK_RCTRL +#define SDLK_RCTRL 305 +#undef SDLK_LCTRL +#define SDLK_LCTRL 306 +#undef SDLK_RALT +#define SDLK_RALT 307 +#undef SDLK_LALT +#define SDLK_LALT 308 +#undef SDLK_RSUPER +#define SDLK_RSUPER 312 +#undef SDLK_LSUPER +#define SDLK_LSUPER 311 +#undef SDLK_MODE +#define SDLK_MODE 313 +#undef SDLK_COMPOSE +#define SDLK_COMPOSE 314 +#undef SDLK_PRINT +#define SDLK_PRINT 316 +#undef SDLK_MENU +#define SDLK_MENU 319 +#define SDLK_WORLD_69 229 +#define SDLK_WORLD_70 230 +#define SDLK_WORLD_88 248 + +#endif