diff --git a/src/config_file.c b/src/config_file.c index f354c5c..58b0841 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -50,7 +50,11 @@ static void configfile_reset_values (); void configfile_reset_values () { +#ifdef __EMSCRIPTEN__ + power_conf->fullscreen = FALSE; +#else power_conf->fullscreen = TRUE; +#endif power_conf->nosound = FALSE; power_conf->resolution = 640; power_conf->verbose = 0; @@ -279,6 +283,9 @@ configfile_load (void) { power_conf->fullscreen = TRUE; } +#ifdef __EMSCRIPTEN__ + power_conf->fullscreen = FALSE; +#endif if (!lisp_read_bool (lst, "nosound", &power_conf->nosound)) { power_conf->nosound = FALSE; @@ -621,3 +628,18 @@ configfile_get_lang (void) { return lang_to_text[power_conf->lang]; } + +#ifdef __EMSCRIPTEN__ +/** + * Called by the web page when the tab is hidden or closed: the game only + * saves its configuration when it quits + */ +__attribute__ ((used, visibility ("default"))) void +fredygames_save (void) +{ + if (configname != NULL) + { + configfile_save (); + } +} +#endif diff --git a/src/display_sdl.c b/src/display_sdl.c index 734e514..e114bee 100644 --- a/src/display_sdl.c +++ b/src/display_sdl.c @@ -38,6 +38,10 @@ #include "log_recorder.h" #include "options_panel.h" #include "gfx_wrapper.h" +#ifndef KEY_STATE +#define KEY_STATE(k, sym) ((k)[sym]) +#define KEYSYM_UNICODE(ke) ((ke)->keysym.unicode) +#endif #ifdef USE_SCALE2X #include "scalebit.h" #endif @@ -899,12 +903,12 @@ display_handle_events (void) ke = (SDL_KeyboardEvent *) & event; /* LOG_INF ("SDL_KEYDOWN: " "%i %i %i %i", ke->type, ke->keysym.sym, - ke->keysym.unicode, ke->state); */ + KEYSYM_UNICODE (ke), ke->state); */ keys = SDL_GetKeyState (NULL); key_status (keys); - if (ke->keysym.unicode > 0) + if (KEYSYM_UNICODE (ke) > 0) { - sprites_string_key_down (ke->keysym.unicode, ke->keysym.sym); + sprites_string_key_down (KEYSYM_UNICODE (ke), ke->keysym.sym); } else { @@ -920,11 +924,11 @@ display_handle_events (void) ke = (SDL_KeyboardEvent *) & event; /* LOG_INF ("SDL_KEYUP: " "%i %i %i %i\n", ke->type, ke->keysym.sym, - ke->keysym.unicode, ke->state); */ + KEYSYM_UNICODE (ke), ke->state); */ keys = SDL_GetKeyState (NULL); - if (ke->keysym.unicode > 0) + if (KEYSYM_UNICODE (ke) > 0) { - sprites_string_key_up (ke->keysym.unicode, ke->keysym.sym); + sprites_string_key_up (KEYSYM_UNICODE (ke), ke->keysym.sym); } else { @@ -1116,6 +1120,25 @@ display_handle_events (void) mouse_b = 0; break; } +#ifdef __EMSCRIPTEN__ + break; + + /* application loses/gains focus, screen needs to be redrawn */ + case SDL_WINDOWEVENT: + switch (event.window.event) + { + case SDL_WINDOWEVENT_FOCUS_LOST: + display_toggle_pause (0); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + display_toggle_pause (1); + break; + case SDL_WINDOWEVENT_EXPOSED: + update_all = TRUE; + break; + } + break; +#else /* application loses/gains visibility */ case SDL_ACTIVEEVENT: @@ -1150,6 +1173,7 @@ display_handle_events (void) SDL_GetError ()); } break; +#endif /* mouse moved */ case SDL_MOUSEMOTION: @@ -1169,87 +1193,87 @@ display_handle_events (void) void key_status (Uint8 * k) { - keys_down[K_ESCAPE] = k[SDLK_ESCAPE]; - keys_down[K_CTRL] = k[SDLK_LCTRL]; - keys_down[K_CTRL] |= k[SDLK_RCTRL]; - keys_down[K_RETURN] = k[SDLK_RETURN]; - keys_down[K_PAUSE] = k[SDLK_PAUSE]; - keys_down[K_SHIFT] = k[SDLK_LSHIFT]; - keys_down[K_SHIFT] |= k[SDLK_RSHIFT]; - keys_down[K_1] = k[SDLK_1] | k[SDLK_KP1]; - keys_down[K_2] = k[SDLK_2] | k[SDLK_KP2]; - keys_down[K_3] = k[SDLK_3] | k[SDLK_KP3]; - keys_down[K_4] = k[SDLK_4] | k[SDLK_KP4]; - keys_down[K_5] = k[SDLK_5] | k[SDLK_KP5]; - keys_down[K_6] = k[SDLK_6] | k[SDLK_KP6]; - keys_down[K_7] = k[SDLK_7] | k[SDLK_KP7]; - keys_down[K_8] = k[SDLK_8] | k[SDLK_KP8]; - keys_down[K_9] = k[SDLK_9] | k[SDLK_KP9]; - keys_down[K_0] = k[SDLK_0] | k[SDLK_KP0]; - keys_down[K_F1] = k[SDLK_F1]; - keys_down[K_F2] = k[SDLK_F2]; - keys_down[K_F3] = k[SDLK_F3]; - keys_down[K_F4] = k[SDLK_F4]; - keys_down[K_F5] = k[SDLK_F5]; - keys_down[K_F6] = k[SDLK_F6]; - keys_down[K_F7] = k[SDLK_F7]; - keys_down[K_F8] = k[SDLK_F8]; - keys_down[K_F9] = k[SDLK_F9]; - keys_down[K_F10] = k[SDLK_F10]; - keys_down[K_F11] = k[SDLK_F11]; - keys_down[K_F12] = k[SDLK_F12]; - keys_down[K_INSERT] = k[SDLK_INSERT]; - keys_down[K_SPACE] = k[SDLK_SPACE]; + keys_down[K_ESCAPE] = KEY_STATE (k, SDLK_ESCAPE); + keys_down[K_CTRL] = KEY_STATE (k, SDLK_LCTRL); + keys_down[K_CTRL] |= KEY_STATE (k, SDLK_RCTRL); + keys_down[K_RETURN] = KEY_STATE (k, SDLK_RETURN); + keys_down[K_PAUSE] = KEY_STATE (k, SDLK_PAUSE); + keys_down[K_SHIFT] = KEY_STATE (k, SDLK_LSHIFT); + keys_down[K_SHIFT] |= KEY_STATE (k, SDLK_RSHIFT); + keys_down[K_1] = KEY_STATE (k, SDLK_1) | KEY_STATE (k, SDLK_KP1); + keys_down[K_2] = KEY_STATE (k, SDLK_2) | KEY_STATE (k, SDLK_KP2); + keys_down[K_3] = KEY_STATE (k, SDLK_3) | KEY_STATE (k, SDLK_KP3); + keys_down[K_4] = KEY_STATE (k, SDLK_4) | KEY_STATE (k, SDLK_KP4); + keys_down[K_5] = KEY_STATE (k, SDLK_5) | KEY_STATE (k, SDLK_KP5); + keys_down[K_6] = KEY_STATE (k, SDLK_6) | KEY_STATE (k, SDLK_KP6); + keys_down[K_7] = KEY_STATE (k, SDLK_7) | KEY_STATE (k, SDLK_KP7); + keys_down[K_8] = KEY_STATE (k, SDLK_8) | KEY_STATE (k, SDLK_KP8); + keys_down[K_9] = KEY_STATE (k, SDLK_9) | KEY_STATE (k, SDLK_KP9); + keys_down[K_0] = KEY_STATE (k, SDLK_0) | KEY_STATE (k, SDLK_KP0); + keys_down[K_F1] = KEY_STATE (k, SDLK_F1); + keys_down[K_F2] = KEY_STATE (k, SDLK_F2); + keys_down[K_F3] = KEY_STATE (k, SDLK_F3); + keys_down[K_F4] = KEY_STATE (k, SDLK_F4); + keys_down[K_F5] = KEY_STATE (k, SDLK_F5); + keys_down[K_F6] = KEY_STATE (k, SDLK_F6); + keys_down[K_F7] = KEY_STATE (k, SDLK_F7); + keys_down[K_F8] = KEY_STATE (k, SDLK_F8); + keys_down[K_F9] = KEY_STATE (k, SDLK_F9); + keys_down[K_F10] = KEY_STATE (k, SDLK_F10); + keys_down[K_F11] = KEY_STATE (k, SDLK_F11); + keys_down[K_F12] = KEY_STATE (k, SDLK_F12); + keys_down[K_INSERT] = KEY_STATE (k, SDLK_INSERT); + keys_down[K_SPACE] = KEY_STATE (k, SDLK_SPACE); if (is_reverse_ctrl) { - keys_down[K_LEFT] = k[SDLK_DOWN]; - keys_down[K_RIGHT] = k[SDLK_UP]; - keys_down[K_UP] = k[SDLK_LEFT]; - keys_down[K_DOWN] = k[SDLK_RIGHT]; + keys_down[K_LEFT] = KEY_STATE (k, SDLK_DOWN); + keys_down[K_RIGHT] = KEY_STATE (k, SDLK_UP); + keys_down[K_UP] = KEY_STATE (k, SDLK_LEFT); + keys_down[K_DOWN] = KEY_STATE (k, SDLK_RIGHT); } else { - keys_down[K_LEFT] = k[SDLK_LEFT]; - keys_down[K_RIGHT] = k[SDLK_RIGHT]; - keys_down[K_UP] = k[SDLK_UP]; - keys_down[K_DOWN] = k[SDLK_DOWN]; + keys_down[K_LEFT] = KEY_STATE (k, SDLK_LEFT); + keys_down[K_RIGHT] = KEY_STATE (k, SDLK_RIGHT); + keys_down[K_UP] = KEY_STATE (k, SDLK_UP); + keys_down[K_DOWN] = KEY_STATE (k, SDLK_DOWN); } /* [Ctrl] + [A]: ABOUT */ - keys_down[K_A] = k[SDLK_a]; + keys_down[K_A] = KEY_STATE (k, SDLK_a); /* switch between full screen and windowed mode */ - keys_down[K_F] = k[SDLK_f]; - keys_down[K_V] = k[SDLK_v]; - keys_down[K_B] = k[SDLK_b]; + keys_down[K_F] = KEY_STATE (k, SDLK_f); + keys_down[K_V] = KEY_STATE (k, SDLK_v); + keys_down[K_B] = KEY_STATE (k, SDLK_b); /* enable/disable pause */ - keys_down[K_P] = k[SDLK_p]; + keys_down[K_P] = KEY_STATE (k, SDLK_p); /* [Ctrl] + [Q]: force "Game Over" */ - keys_down[K_Q] = k[SDLK_q]; + keys_down[K_Q] = KEY_STATE (k, SDLK_q); /* [Ctrl] + [S]: enable/disable the music */ - keys_down[K_S] = k[SDLK_s]; + keys_down[K_S] = KEY_STATE (k, SDLK_s); /* right */ - keys_down[K_RIGHT] |= k[SDLK_KP6]; + keys_down[K_RIGHT] |= KEY_STATE (k, SDLK_KP6); /* left */ - keys_down[K_LEFT] |= k[SDLK_KP4]; + keys_down[K_LEFT] |= KEY_STATE (k, SDLK_KP4); /* up */ - keys_down[K_UP] |= k[SDLK_KP8]; + keys_down[K_UP] |= KEY_STATE (k, SDLK_KP8); /* down */ - keys_down[K_DOWN] |= k[SDLK_KP5]; + keys_down[K_DOWN] |= KEY_STATE (k, SDLK_KP5); /* power-up (aka Ctrl key) */ - keys_down[K_CTRL] |= k[SDLK_KP2]; + keys_down[K_CTRL] |= KEY_STATE (k, SDLK_KP2); /* fire (aka space bar) */ - keys_down[K_SPACE] |= k[SDLK_KP0]; + keys_down[K_SPACE] |= KEY_STATE (k, SDLK_KP0); /* fire (aka space ENTER) */ - if (k[SDLK_RETURN] && !is_playername_input () && + if (KEY_STATE (k, SDLK_RETURN) && !is_playername_input () && menu_section != SECTION_ORDER) { - keys_down[K_SPACE] |= k[SDLK_RETURN]; + keys_down[K_SPACE] |= KEY_STATE (k, SDLK_RETURN); } - keys_down[K_C] = k[SDLK_c]; - keys_down[K_G] = k[SDLK_g]; - keys_down[K_E] = k[SDLK_e]; + keys_down[K_C] = KEY_STATE (k, SDLK_c); + keys_down[K_G] = KEY_STATE (k, SDLK_g); + keys_down[K_E] = KEY_STATE (k, SDLK_e); /* Volume control */ - keys_down[K_PAGEUP] = k[SDLK_PAGEUP]; - keys_down[K_PAGEDOWN] = k[SDLK_PAGEDOWN]; + keys_down[K_PAGEUP] = KEY_STATE (k, SDLK_PAGEUP); + keys_down[K_PAGEDOWN] = KEY_STATE (k, SDLK_PAGEDOWN); } /** diff --git a/src/powermanga.c b/src/powermanga.c index f8eb207..dec1bfd 100644 --- a/src/powermanga.c +++ b/src/powermanga.c @@ -362,7 +362,7 @@ update_frame (void) /* switch between full screen and windowed mode */ -#ifdef POWERMANGA_SDL +#if defined(POWERMANGA_SDL) && !defined(__EMSCRIPTEN__) if ((keys_down[K_F] && !is_playername_input () && menu_section != SECTION_ORDER) || keys_down[K_F11]) { diff --git a/src/powermanga.h b/src/powermanga.h index 3f0ad1b..5fcaa49 100644 --- a/src/powermanga.h +++ b/src/powermanga.h @@ -137,7 +137,10 @@ typedef unsigned int Uint32; /** Else use SDL */ #else -#if defined(_WIN32_WCE) || defined(_WIN32) +#if defined(__EMSCRIPTEN__) +/* web browser: SDL2 with the part of the SDL 1.2 API the game uses */ +#include "sdl1compat.h" +#elif defined(_WIN32_WCE) || defined(_WIN32) #include #else #include @@ -158,7 +161,7 @@ typedef unsigned int Uint32; #undef USE_SDLMIXER #endif -#ifdef USE_SDLMIXER +#if defined(USE_SDLMIXER) && !defined(__EMSCRIPTEN__) #if defined(POWERMANGA_X11) #include #endif diff --git b/src/sdl1compat.c b/src/sdl1compat.c new file mode 100644 index 0000000..0ba46e0 --- /dev/null +++ b/src/sdl1compat.c @@ -0,0 +1,167 @@ +/** + * @file sdl1compat.c + * @brief The part of the SDL 1.2 API that Powermanga uses, on top of SDL2 + * (fredygames.com browser build, with Emscripten). + * The game screen is an XRGB8888 surface copied to a texture on + * every frame. + * @license GNU GPL v3 + */ +#include "config.h" +#include "powermanga.h" + +#ifdef __EMSCRIPTEN__ + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Texture *texture = NULL; +static SDL_Surface *screen = NULL; +static SDL_PixelFormat *screen_format = NULL; +static SDL_VideoInfo video_info; + +const SDL_VideoInfo * +SDL_GetVideoInfo (void) +{ + if (screen_format == NULL) + { + screen_format = SDL_AllocFormat (SDL_PIXELFORMAT_RGB888); + } + SDL_memset (&video_info, 0, sizeof (video_info)); + video_info.wm_available = 1; + video_info.vfmt = screen_format; + return &video_info; +} + +SDL_Rect ** +SDL_ListModes (SDL_PixelFormat * format, Uint32 flags) +{ + /* any size is fine */ + return (SDL_Rect **) - 1; +} + +int +SDL_VideoModeOK (int width, int height, int bpp, Uint32 flags) +{ + return 32; +} + +SDL_Surface * +SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags) +{ + /* full screen is handled by the web page */ + if (window == NULL) + { + window = SDL_CreateWindow ("Powermanga", 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 (screen != NULL && (screen->w != width || screen->h != height)) + { + SDL_FreeSurface (screen); + SDL_DestroyTexture (texture); + screen = NULL; + texture = NULL; + } + if (screen == NULL) + { + SDL_RenderSetLogicalSize (renderer, width, height); + texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_STREAMING, width, + height); + screen = SDL_CreateRGBSurfaceWithFormat (0, width, height, 32, + SDL_PIXELFORMAT_RGB888); + if (texture == NULL || screen == NULL) + { + return NULL; + } + } + return screen; +} + +int +SDL_Flip (SDL_Surface * surface) +{ + if (surface != screen || texture == NULL) + { + return 0; + } + SDL_UpdateTexture (texture, NULL, screen->pixels, screen->pitch); + SDL_RenderClear (renderer); + SDL_RenderCopy (renderer, texture, NULL, NULL); + SDL_RenderPresent (renderer); + return 0; +} + +void +SDL_UpdateRect (SDL_Surface * surface, Sint32 x, Sint32 y, Uint32 w, + Uint32 h) +{ + SDL_Flip (surface); +} + +int +SDL_SetPalette (SDL_Surface * surface, int flags, const SDL_Color * colors, + int firstcolor, int ncolors) +{ + if (surface == NULL || surface->format->palette == NULL) + { + return 0; + } + return SDL_SetPaletteColors (surface->format->palette, colors, firstcolor, + ncolors) == 0; +} + +void +SDL_WM_SetCaption (const char *title, const char *icon) +{ + if (window != NULL) + { + SDL_SetWindowTitle (window, title); + } +} + +char * +SDL_VideoDriverName (char *namebuf, int maxlen) +{ + const char *name = SDL_GetCurrentVideoDriver (); + if (name == NULL) + { + return NULL; + } + SDL_strlcpy (namebuf, name, maxlen); + return namebuf; +} + +int +SDL_EnableUNICODE (int enable) +{ + return 0; +} + +Uint16 +sdl1_keysym_unicode (const SDL_Keysym * keysym) +{ + SDL_Keycode sym = keysym->sym; + if (sym < 32 || sym > 126) + { + return 0; + } + if ((keysym->mod & KMOD_SHIFT) && sym >= 'a' && sym <= 'z') + { + return (Uint16) (sym - 'a' + 'A'); + } + return (Uint16) sym; +} + +#endif diff --git b/src/sdl1compat.h b/src/sdl1compat.h new file mode 100644 index 0000000..6546df6 --- /dev/null +++ b/src/sdl1compat.h @@ -0,0 +1,68 @@ +/** + * @file sdl1compat.h + * @brief The part of the SDL 1.2 API that Powermanga uses, on top of SDL2 + * (fredygames.com browser build, with Emscripten) + * @license GNU GPL v3 + */ +#ifndef __SDL1COMPAT__ +#define __SDL1COMPAT__ + +#include +#include +#include + +/* SDL_SetVideoMode and SDL_SetPalette flags */ +#define SDL_ANYFORMAT 0x10000000 +#define SDL_HWPALETTE 0x20000000 +#define SDL_FULLSCREEN 0x80000000 +#define SDL_LOGPAL 0x01 +#define SDL_PHYSPAL 0x02 + +/* keypad keys with their SDL 1.2 names */ +#define SDLK_KP0 SDLK_KP_0 +#define SDLK_KP1 SDLK_KP_1 +#define SDLK_KP2 SDLK_KP_2 +#define SDLK_KP3 SDLK_KP_3 +#define SDLK_KP4 SDLK_KP_4 +#define SDLK_KP5 SDLK_KP_5 +#define SDLK_KP6 SDLK_KP_6 +#define SDLK_KP7 SDLK_KP_7 +#define SDLK_KP8 SDLK_KP_8 +#define SDLK_KP9 SDLK_KP_9 + +typedef struct +{ + Uint32 hw_available:1; + Uint32 wm_available:1; + Uint32 blit_hw:1; + Uint32 blit_hw_CC:1; + Uint32 blit_hw_A:1; + Uint32 blit_sw:1; + Uint32 blit_sw_CC:1; + Uint32 blit_sw_A:1; + Uint32 blit_fill:1; + Uint32 video_mem; + SDL_PixelFormat *vfmt; +} SDL_VideoInfo; + +const SDL_VideoInfo *SDL_GetVideoInfo (void); +SDL_Rect **SDL_ListModes (SDL_PixelFormat * format, Uint32 flags); +int SDL_VideoModeOK (int width, int height, int bpp, Uint32 flags); +SDL_Surface *SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags); +int SDL_Flip (SDL_Surface * screen); +void SDL_UpdateRect (SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, + Uint32 h); +int SDL_SetPalette (SDL_Surface * surface, int flags, + const SDL_Color * colors, int firstcolor, int ncolors); +void SDL_WM_SetCaption (const char *title, const char *icon); +char *SDL_VideoDriverName (char *namebuf, int maxlen); +int SDL_EnableUNICODE (int enable); + +/* SDL2 indexes the keyboard state by scancode, not by keycode */ +#define SDL_GetKeyState(numkeys) ((Uint8 *) SDL_GetKeyboardState (numkeys)) +#define KEY_STATE(k, sym) ((k)[SDL_GetScancodeFromKey (sym)]) +/* SDL2 has no keysym.unicode: the character comes from the key itself */ +Uint16 sdl1_keysym_unicode (const SDL_Keysym * keysym); +#define KEYSYM_UNICODE(ke) sdl1_keysym_unicode (&(ke)->keysym) + +#endif diff --git a/src/tools.c b/src/tools.c index 07fbad0..ad59efd 100644 --- a/src/tools.c +++ b/src/tools.c @@ -28,6 +28,9 @@ #include "log_recorder.h" #include "tools.h" #include "config_file.h" +#ifdef __EMSCRIPTEN__ +#include +#endif #include #if defined (USE_MALLOC_WRAPPER) @@ -1142,6 +1145,13 @@ wait_next_frame (Sint32 delay, Sint32 max) { SDL_Delay (delay); } +#ifdef __EMSCRIPTEN__ + else + { + /* late frame: give control back to the browser anyway */ + emscripten_sleep (0); + } +#endif return (delay > 0 ? delay : 0); #else struct timeval temps;