/* * tbzsdl12.c: the part of the SDL 1.2 API that TecnoballZ uses, on top of SDL2, for the fredygames.com * browser build (Emscripten). See tbzsdl12.h. * * The game draws into an 8-bit, palettized SDL_Surface (the "screen" of SDL_SetVideoMode). Each * SDL_UpdateRect/SDL_Flip converts it to 32 bits, uploads it to a streaming texture and presents it, * then gives the browser a turn (the build uses Asyncify). * * Copyright (C) 2026 FredyGames. GNU GPL version 3 or later, like TecnoballZ. */ #include "tbzsdl12.h" #include #ifdef __EMSCRIPTEN__ #include #endif #undef SDL_PollEvent #undef SDL_CreateRGBSurface #undef unicode static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; static SDL_Texture *texture = NULL; static SDL_Surface *screen = NULL; static SDL_Surface *rgb = NULL; static Uint8 keystate[1024]; /* folds an SDL2 key symbol to the index the game uses (see tbzsdl12.h) */ static int fold_key (SDL_Keycode sym, SDL_Scancode scancode) { if (sym >= 0 && sym < 256) return sym; return 256 + (int) scancode; } const SDL_VideoInfo * SDL_GetVideoInfo (void) { static SDL_VideoInfo info; return &info; } SDL_Rect ** SDL_ListModes (SDL_PixelFormat * format, Uint32 flags) { (void) format; (void) flags; return (SDL_Rect **) - 1; } int SDL_VideoModeOK (int width, int height, int bpp, Uint32 flags) { (void) width; (void) height; (void) flags; return bpp > 0 ? bpp : 8; } SDL_Surface * SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags) { (void) flags; if (window == NULL) { window = SDL_CreateWindow ("TecnoballZ", 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; } else SDL_SetWindowSize (window, width, height); if (texture != NULL) SDL_DestroyTexture (texture); if (screen != NULL) SDL_FreeSurface (screen); if (rgb != NULL) SDL_FreeSurface (rgb); texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); screen = SDL_CreateRGBSurfaceWithFormat (0, width, height, bpp > 0 ? bpp : 8, bpp == 8 || bpp == 0 ? SDL_PIXELFORMAT_INDEX8 : SDL_PIXELFORMAT_ARGB8888); rgb = SDL_CreateRGBSurfaceWithFormat (0, width, height, 32, SDL_PIXELFORMAT_ARGB8888); return screen; } int SDL_Flip (SDL_Surface * surface) { if (surface != screen || texture == NULL) return -1; SDL_BlitSurface (screen, NULL, rgb, NULL); SDL_UpdateTexture (texture, NULL, rgb->pixels, rgb->pitch); SDL_RenderClear (renderer); SDL_RenderCopy (renderer, texture, NULL, NULL); SDL_RenderPresent (renderer); #ifdef __EMSCRIPTEN__ emscripten_sleep (0); #endif return 0; } void SDL_UpdateRect (SDL_Surface * surface, Sint32 x, Sint32 y, Uint32 w, Uint32 h) { (void) x; (void) y; (void) w; (void) h; SDL_Flip (surface); } int SDL_SetPalette (SDL_Surface * surface, int flags, const SDL_Color * colors, int firstcolor, int ncolors) { SDL_Color full[256]; int i; (void) flags; if (surface == NULL || surface->format->palette == NULL) return 0; /* SDL 1.2 ignores the alpha of palette colors; SDL2 uses it, so every color is made opaque */ if (ncolors > 256 - firstcolor) ncolors = 256 - firstcolor; for (i = 0; i < ncolors; i++) { full[i] = colors[i]; full[i].a = 255; } return SDL_SetPaletteColors (surface->format->palette, full, firstcolor, ncolors) == 0; } void SDL_WM_SetCaption (const char *title, const char *icon) { (void) icon; if (window != NULL && title != NULL) SDL_SetWindowTitle (window, title); } SDL_GrabMode SDL_WM_GrabInput (SDL_GrabMode mode) { /* in the browser, grabbing means pointer lock: the paddle follows the relative mouse motion */ if (mode == SDL_GRAB_QUERY) return SDL_GetRelativeMouseMode ()? SDL_GRAB_ON : SDL_GRAB_OFF; SDL_SetRelativeMouseMode (mode == SDL_GRAB_ON ? SDL_TRUE : SDL_FALSE); return mode; } void SDL_WarpMouse (Uint16 x, Uint16 y) { if (window != NULL) SDL_WarpMouseInWindow (window, x, y); } char * SDL_VideoDriverName (char *namebuf, int maxlen) { const char *name = SDL_GetCurrentVideoDriver (); if (namebuf == NULL || maxlen <= 0 || name == NULL) return NULL; strncpy (namebuf, name, maxlen - 1); namebuf[maxlen - 1] = '\0'; return namebuf; } int SDL_EnableUNICODE (int enable) { (void) enable; return 1; } Uint8 * SDL_GetKeyState (int *numkeys) { if (numkeys != NULL) *numkeys = (int) sizeof (keystate); return keystate; } /* the character a key types, as SDL 1.2 put it in keysym.unicode (only what the game's text input * needs: letters, digits and a few signs; shift gives capitals) */ static Uint32 key_character (const SDL_Keysym * keysym) { SDL_Keycode sym = keysym->sym; int shift = (keysym->mod & KMOD_SHIFT) != 0; if (sym >= SDLK_a && sym <= SDLK_z) return shift ? (Uint32) (sym - 'a' + 'A') : (Uint32) sym; if (sym >= 32 && sym < 127) return (Uint32) sym; return 0; } int tbz_PollEvent (SDL_Event * event) { int got = SDL_PollEvent (event); if (got && event != NULL && (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP)) { int index = fold_key (event->key.keysym.sym, event->key.keysym.scancode); if (index >= 0 && index < (int) sizeof (keystate)) keystate[index] = event->type == SDL_KEYDOWN ? SDL_PRESSED : SDL_RELEASED; event->key.keysym.unused = key_character (&event->key.keysym); event->key.keysym.sym = index; } return got; } SDL_Surface * tbz_CreateRGBSurface (Uint32 flags, int width, int height, int depth, Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask) { (void) flags; if (depth <= 8) return SDL_CreateRGBSurfaceWithFormat (0, width, height, 8, SDL_PIXELFORMAT_INDEX8); return SDL_CreateRGBSurface (0, width, height, depth, rmask, gmask, bmask, amask); }