#include <SDL2/SDL.h>#include <SDL2/SDL_image.h>#include <SDL2/SDL_mixer.h> #include "basis.h"#include "printsdlerror.h" /*Copyright (c) 2013-2021 Devine Lu Linvega2018 web port by rezmason2021 C89 port by rezmason Permission to use, copy, modify, and distribute this software for anypurpose with or without fee is hereby granted, provided that the abovecopyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIESWITH REGARD TO THIS SOFTWARE.*/ /* The game loop's methods are made accessible to the event watch through this type */typedef struct { void (*resize)(int, int); void (*update)(double, double); void (*render)(SDL_Renderer *);} WatchContext; static SDL_Window *_window = NULL;static SDL_Renderer *_renderer = NULL; static unsigned long _now = 0;static unsigned long _start = 0;static unsigned long _last = 0;static double _totalTime = 0;static double _deltaTime = 0;static double _performanceFrequency = 1;static SDL_Event _event; static int _quitting = 0;static int _focused = 0; static intinit(int chunkSize){ int sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO; int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG; int mixFlags = MIX_INIT_MP3; if(SDL_Init(sdlFlags) < 0) { return printSDLError("SDL could not initialize."); } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); if(IMG_Init(imgFlags) != imgFlags) { return printSDLError("SDL_image could not initialize."); } if(Mix_Init(mixFlags) != mixFlags) { return printSDLError("SDL_mixer could not initialize."); } if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY /*44100*/, MIX_DEFAULT_FORMAT, 2, chunkSize) < 0) { return printSDLError("SDL_mixer could not initialize (Mix_OpenAudio)."); } return 1;} /* When the game first initializes, this method fits its window to the screen so that it is proportional and as wide as possible.*/static voidfitWindowToScreen(int *width, int *height){ SDL_DisplayMode displayMode; if(SDL_GetCurrentDisplayMode(0, &displayMode) != 0) { printSDLError("SDL could not get current display mode."); } if(*width > displayMode.w || *height > displayMode.h) { float aspectRatio = (float)*width / (float)*height; float displayAspectRatio = (float)displayMode.w / (float)displayMode.h; if(displayAspectRatio < aspectRatio) { *width = displayMode.w / 128 * 128; *height = *width / aspectRatio; } else { *height = displayMode.h / 128 * 128; *width = *height * aspectRatio; } }} static intcreateWindow(char *name, int *windowW, int *windowH, int fitToScreen){ int flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; SDL_DestroyWindow(_window); _window = NULL; SDL_DestroyRenderer(_renderer); _renderer = NULL; if(fitToScreen) { fitWindowToScreen(windowW, windowH); } _window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, *windowW, *windowH, flags); if(_window == NULL) { return printSDLError("SDL could not create a window."); } _renderer = SDL_CreateRenderer( _window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if(_renderer == NULL) { return printSDLError("SDL could not create a renderer."); } SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 0xFF); return 1;} static voidupdateTime(){ _last = _now; _now = SDL_GetPerformanceCounter(); _totalTime = (double)(_now - _start) / _performanceFrequency; _deltaTime = (double)(_now - _last) / _performanceFrequency;} /* SDL games usually freeze when you manually resize their window. This hack circumvents the game loop to allow us to at least re-render the contents of the window as the player adjusts its dimensions.*/static inteventWatch(void *data, SDL_Event *event){ if(event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { WatchContext *watchContext = data; SDL_Delay(10); watchContext->resize(event->window.data1, event->window.data2); if(_focused) {#if 0 updateTime(); if(_deltaTime < 1) { watchContext->update(_totalTime, _deltaTime); }#endif } SDL_RenderClear(_renderer); watchContext->render(_renderer); SDL_RenderPresent(_renderer); } return 1;} intBasis_Init(char *name, int chunkSize, int *windowW, int *windowH, int fitToScreen){ int status = 1; status &= init(chunkSize); status &= createWindow(name, windowW, windowH, fitToScreen); return status;} voidBasis_Halt(){ _quitting = 1;} voidBasis_Quit(){ Basis_Halt(); SDL_DestroyRenderer(_renderer); _renderer = NULL; SDL_DestroyWindow(_window); _window = NULL; Mix_CloseAudio(); IMG_Quit(); Mix_Quit(); SDL_Quit();} SDL_Renderer *Basis_GetRenderer(){ return _renderer;} /* Basis_Run encapsulates a core game loop. It also supplies the values that are then passed on to its handlers, such as the game time, events, and the window renderer. Lastly, it registers the event watch for the resize hack above.*/voidBasis_Run(void (*processEvent)(SDL_Event *), void (*resize)(int, int), void (*update)(double, double), void (*render)(SDL_Renderer *)){ int windowW, windowH; WatchContext watchContext; _performanceFrequency = (double)SDL_GetPerformanceFrequency(); _start = SDL_GetPerformanceCounter(); _quitting = 0; _focused = 0; watchContext.resize = resize; watchContext.update = update; watchContext.render = render; SDL_AddEventWatch(&eventWatch, &watchContext); SDL_GetWindowSize(_window, &windowW, &windowH); resize(windowW, windowH); while(!_quitting) { while(SDL_PollEvent(&_event) != 0) { switch(_event.type) { case SDL_QUIT: _quitting = 1; break; case SDL_WINDOWEVENT: switch(_event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: resize(_event.window.data1, _event.window.data2); break; case SDL_WINDOWEVENT_FOCUS_GAINED: _focused = 1; break; case SDL_WINDOWEVENT_FOCUS_LOST: _focused = 0; break; } break; case SDL_KEYDOWN: if(!_event.key.repeat) { processEvent(&_event); } break; case SDL_KEYUP: if(!_event.key.repeat) { processEvent(&_event); } break; case SDL_MOUSEMOTION: processEvent(&_event); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if(_event.button.button == SDL_BUTTON_LEFT) { processEvent(&_event); } break; } } if(_focused) { updateTime(); if(_deltaTime < 1) { update(_totalTime, _deltaTime); } } SDL_RenderClear(_renderer); render(_renderer); SDL_RenderPresent(_renderer); } SDL_DelEventWatch(&eventWatch, &watchContext);}
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include "basis.h"
#include "printsdlerror.h"
/*
Copyright (c) 2013-2021 Devine Lu Linvega
2018 web port by rezmason
2021 C89 port by rezmason
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE.
*/
/* The game loop's methods are made accessible to the event watch through this type */
typedef struct {
void (*resize)(int, int);
void (*update)(double, double);
void (*render)(SDL_Renderer *);
} WatchContext;
static SDL_Window *_window = NULL;
static SDL_Renderer *_renderer = NULL;
static unsigned long _now = 0;
static unsigned long _start = 0;
static unsigned long _last = 0;
static double _totalTime = 0;
static double _deltaTime = 0;
static double _performanceFrequency = 1;
static SDL_Event _event;
static int _quitting = 0;
static int _focused = 0;
static int
init(int chunkSize)
{
int sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
int mixFlags = MIX_INIT_MP3;
if(SDL_Init(sdlFlags) < 0) {
return printSDLError("SDL could not initialize.");
}
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
if(IMG_Init(imgFlags) != imgFlags) {
return printSDLError("SDL_image could not initialize.");
}
if(Mix_Init(mixFlags) != mixFlags) {
return printSDLError("SDL_mixer could not initialize.");
}
if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY /*44100*/, MIX_DEFAULT_FORMAT, 2, chunkSize) < 0) {
return printSDLError("SDL_mixer could not initialize (Mix_OpenAudio).");
}
return 1;
}
/*
When the game first initializes, this method fits its window to the screen
so that it is proportional and as wide as possible.
*/
static void
fitWindowToScreen(int *width, int *height)
{
SDL_DisplayMode displayMode;
if(SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
printSDLError("SDL could not get current display mode.");
}
if(*width > displayMode.w || *height > displayMode.h) {
float aspectRatio = (float)*width / (float)*height;
float displayAspectRatio = (float)displayMode.w / (float)displayMode.h;
if(displayAspectRatio < aspectRatio) {
*width = displayMode.w / 128 * 128;
*height = *width / aspectRatio;
} else {
*height = displayMode.h / 128 * 128;
*width = *height * aspectRatio;
}
}
}
static int
createWindow(char *name, int *windowW, int *windowH, int fitToScreen)
{
int flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
SDL_DestroyWindow(_window);
_window = NULL;
SDL_DestroyRenderer(_renderer);
_renderer = NULL;
if(fitToScreen) {
fitWindowToScreen(windowW, windowH);
}
_window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, *windowW, *windowH, flags);
if(_window == NULL) {
return printSDLError("SDL could not create a window.");
}
_renderer = SDL_CreateRenderer(
_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if(_renderer == NULL) {
return printSDLError("SDL could not create a renderer.");
}
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 0xFF);
return 1;
}
static void
updateTime()
{
_last = _now;
_now = SDL_GetPerformanceCounter();
_totalTime = (double)(_now - _start) / _performanceFrequency;
_deltaTime = (double)(_now - _last) / _performanceFrequency;
}
/*
SDL games usually freeze when you manually resize their window.
This hack circumvents the game loop to allow us to at least
re-render the contents of the window as the player adjusts its dimensions.
*/
static int
eventWatch(void *data, SDL_Event *event)
{
if(event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
WatchContext *watchContext = data;
SDL_Delay(10);
watchContext->resize(event->window.data1, event->window.data2);
if(_focused) {
#if 0
updateTime();
if(_deltaTime < 1) {
watchContext->update(_totalTime, _deltaTime);
}
#endif
}
SDL_RenderClear(_renderer);
watchContext->render(_renderer);
SDL_RenderPresent(_renderer);
}
return 1;
}
int
Basis_Init(char *name, int chunkSize, int *windowW, int *windowH, int fitToScreen)
{
int status = 1;
status &= init(chunkSize);
status &= createWindow(name, windowW, windowH, fitToScreen);
return status;
}
void
Basis_Halt()
{
_quitting = 1;
}
void
Basis_Quit()
{
Basis_Halt();
SDL_DestroyRenderer(_renderer);
_renderer = NULL;
SDL_DestroyWindow(_window);
_window = NULL;
Mix_CloseAudio();
IMG_Quit();
Mix_Quit();
SDL_Quit();
}
SDL_Renderer *
Basis_GetRenderer()
{
return _renderer;
}
/*
Basis_Run encapsulates a core game loop. It also supplies
the values that are then passed on to its handlers,
such as the game time, events, and the window renderer.
Lastly, it registers the event watch for the resize hack above.
*/
void
Basis_Run(void (*processEvent)(SDL_Event *),
void (*resize)(int, int),
void (*update)(double, double),
void (*render)(SDL_Renderer *))
{
int windowW, windowH;
WatchContext watchContext;
_performanceFrequency = (double)SDL_GetPerformanceFrequency();
_start = SDL_GetPerformanceCounter();
_quitting = 0;
_focused = 0;
watchContext.resize = resize;
watchContext.update = update;
watchContext.render = render;
SDL_AddEventWatch(&eventWatch, &watchContext);
SDL_GetWindowSize(_window, &windowW, &windowH);
resize(windowW, windowH);
while(!_quitting) {
while(SDL_PollEvent(&_event) != 0) {
switch(_event.type) {
case SDL_QUIT:
_quitting = 1;
break;
case SDL_WINDOWEVENT:
switch(_event.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED:
resize(_event.window.data1, _event.window.data2);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
_focused = 1;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
_focused = 0;
break;
}
break;
case SDL_KEYDOWN:
if(!_event.key.repeat) {
processEvent(&_event);
}
break;
case SDL_KEYUP:
if(!_event.key.repeat) {
processEvent(&_event);
}
break;
case SDL_MOUSEMOTION:
processEvent(&_event);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if(_event.button.button == SDL_BUTTON_LEFT) {
processEvent(&_event);
}
break;
}
}
if(_focused) {
updateTime();
if(_deltaTime < 1) {
update(_totalTime, _deltaTime);
}
}
SDL_RenderClear(_renderer);
render(_renderer);
SDL_RenderPresent(_renderer);
}
SDL_DelEventWatch(&eventWatch, &watchContext);
}