REminiscence/systemstub_sdl.cpp

693 lines
17 KiB
C++
Raw Normal View History

2016-03-20 17:00:00 +01:00
/*
* REminiscence - Flashback interpreter
2018-01-20 17:00:00 +01:00
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
2015-08-02 18:00:00 +02:00
*/
#include <SDL.h>
#include "scaler.h"
2017-11-03 17:00:00 +01:00
#include "screenshot.h"
2015-08-02 18:00:00 +02:00
#include "systemstub.h"
2016-03-20 17:00:00 +01:00
#include "util.h"
2015-08-02 18:00:00 +02:00
2016-05-09 18:00:00 +02:00
static const int kAudioHz = 22050;
2016-08-07 18:00:00 +02:00
2017-11-03 17:00:00 +01:00
static const char *kIconBmp = "icon.bmp";
2016-08-07 18:00:00 +02:00
static const int kJoystickIndex = 0;
2016-05-09 18:00:00 +02:00
static const int kJoystickCommitValue = 3200;
2015-08-02 18:00:00 +02:00
2017-11-03 17:00:00 +01:00
static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB888;
ScalerParameters ScalerParameters::defaults() {
ScalerParameters params;
params.type = kScalerTypeInternal;
params.scaler = &_internalScaler;
params.factor = _internalScaler.factorMin + (_internalScaler.factorMax - _internalScaler.factorMin) / 2;
return params;
}
2016-05-09 18:00:00 +02:00
struct SystemStub_SDL : SystemStub {
SDL_Window *_window;
SDL_Renderer *_renderer;
SDL_Texture *_texture;
2017-06-07 18:00:00 +02:00
int _texW, _texH;
2016-08-07 18:00:00 +02:00
SDL_GameController *_controller;
2016-05-09 18:00:00 +02:00
SDL_PixelFormat *_fmt;
const char *_caption;
2017-11-03 17:00:00 +01:00
uint32_t *_screenBuffer;
2015-08-02 18:00:00 +02:00
bool _fullscreen;
uint8_t _overscanColor;
2017-11-03 17:00:00 +01:00
uint32_t _rgbPalette[256];
2015-08-02 18:00:00 +02:00
int _screenW, _screenH;
SDL_Joystick *_joystick;
bool _fadeOnUpdateScreen;
2016-05-09 18:00:00 +02:00
void (*_audioCbProc)(void *, int16_t *, int);
2015-08-02 18:00:00 +02:00
void *_audioCbData;
2016-03-20 17:00:00 +01:00
int _screenshot;
2017-11-03 17:00:00 +01:00
ScalerType _scalerType;
const Scaler *_scaler;
int _scaleFactor;
2015-08-02 18:00:00 +02:00
virtual ~SystemStub_SDL() {}
2017-11-03 17:00:00 +01:00
virtual void init(const char *title, int w, int h, bool fullscreen, ScalerParameters *scalerParameters);
2015-08-02 18:00:00 +02:00
virtual void destroy();
2016-03-20 17:00:00 +01:00
virtual void setScreenSize(int w, int h);
2015-08-02 18:00:00 +02:00
virtual void setPalette(const uint8_t *pal, int n);
virtual void setPaletteEntry(int i, const Color *c);
virtual void getPaletteEntry(int i, Color *c);
virtual void setOverscanColor(int i);
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch);
virtual void fadeScreen();
virtual void updateScreen(int shakeOffset);
virtual void processEvents();
virtual void sleep(int duration);
virtual uint32_t getTimeStamp();
virtual void startAudio(AudioCallback callback, void *param);
virtual void stopAudio();
virtual uint32_t getOutputSampleRate();
virtual void lockAudio();
virtual void unlockAudio();
void processEvent(const SDL_Event &ev, bool &paused);
2016-05-09 18:00:00 +02:00
void prepareGraphics();
void cleanupGraphics();
2017-11-03 17:00:00 +01:00
void changeGraphics(bool fullscreen, int scaleFactor);
2017-12-06 17:00:00 +01:00
void drawRect(int x, int y, int w, int h, uint8_t color);
2015-08-02 18:00:00 +02:00
};
SystemStub *SystemStub_SDL_create() {
return new SystemStub_SDL();
}
2017-11-03 17:00:00 +01:00
void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, ScalerParameters *scalerParameters) {
2015-08-02 18:00:00 +02:00
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE);
2016-05-09 18:00:00 +02:00
_caption = title;
2015-08-02 18:00:00 +02:00
memset(&_pi, 0, sizeof(_pi));
2018-01-20 17:00:00 +01:00
_window = 0;
_renderer = 0;
_texture = 0;
_fmt = 0;
2016-03-20 17:00:00 +01:00
_screenBuffer = 0;
2015-08-02 18:00:00 +02:00
_fadeOnUpdateScreen = false;
2016-03-20 17:00:00 +01:00
_fullscreen = fullscreen;
2017-11-03 17:00:00 +01:00
_scalerType = scalerParameters->type;
_scaler = scalerParameters->scaler;
_scaleFactor = scalerParameters->factor;
memset(_rgbPalette, 0, sizeof(_rgbPalette));
2016-05-09 18:00:00 +02:00
_screenW = _screenH = 0;
2016-03-20 17:00:00 +01:00
setScreenSize(w, h);
2016-08-07 18:00:00 +02:00
_joystick = 0;
_controller = 0;
if (SDL_NumJoysticks() > 0) {
SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
if (SDL_IsGameController(kJoystickIndex)) {
_controller = SDL_GameControllerOpen(kJoystickIndex);
}
if (!_controller) {
_joystick = SDL_JoystickOpen(kJoystickIndex);
}
}
2016-03-20 17:00:00 +01:00
_screenshot = 1;
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::destroy() {
2016-05-09 18:00:00 +02:00
cleanupGraphics();
2016-08-07 18:00:00 +02:00
if (_controller) {
SDL_GameControllerClose(_controller);
_controller = 0;
}
2016-05-09 18:00:00 +02:00
if (_joystick) {
2015-08-02 18:00:00 +02:00
SDL_JoystickClose(_joystick);
2016-05-09 18:00:00 +02:00
_joystick = 0;
2015-08-02 18:00:00 +02:00
}
SDL_Quit();
}
2016-03-20 17:00:00 +01:00
void SystemStub_SDL::setScreenSize(int w, int h) {
if (_screenW == w && _screenH == h) {
return;
}
2017-06-07 18:00:00 +02:00
cleanupGraphics();
2017-11-03 17:00:00 +01:00
const int screenBufferSize = w * h * sizeof(uint32_t);
_screenBuffer = (uint32_t *)calloc(1, screenBufferSize);
2016-03-20 17:00:00 +01:00
if (!_screenBuffer) {
error("SystemStub_SDL::setScreenSize() Unable to allocate offscreen buffer, w=%d, h=%d", w, h);
}
_screenW = w;
_screenH = h;
2016-05-09 18:00:00 +02:00
prepareGraphics();
2016-03-20 17:00:00 +01:00
}
2015-08-02 18:00:00 +02:00
void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
assert(n <= 256);
for (int i = 0; i < n; ++i) {
uint8_t r = pal[i * 3 + 0];
uint8_t g = pal[i * 3 + 1];
uint8_t b = pal[i * 3 + 2];
2017-11-03 17:00:00 +01:00
_rgbPalette[i] = SDL_MapRGB(_fmt, r, g, b);
2015-08-02 18:00:00 +02:00
}
}
void SystemStub_SDL::setPaletteEntry(int i, const Color *c) {
2017-11-03 17:00:00 +01:00
_rgbPalette[i] = SDL_MapRGB(_fmt, c->r, c->g, c->b);
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::getPaletteEntry(int i, Color *c) {
2017-11-03 17:00:00 +01:00
SDL_GetRGB(_rgbPalette[i], _fmt, &c->r, &c->g, &c->b);
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::setOverscanColor(int i) {
_overscanColor = i;
}
void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) {
2017-12-06 17:00:00 +01:00
if (x < 0) {
x = 0;
} else if (x >= _screenW) {
return;
}
if (y < 0) {
y = 0;
} else if (y >= _screenH) {
return;
}
if (x + w > _screenW) {
w = _screenW - x;
}
if (y + h > _screenH) {
h = _screenH - y;
}
2015-08-02 18:00:00 +02:00
2017-12-06 17:00:00 +01:00
uint32_t *p = _screenBuffer + y * _screenW + x;
buf += y * pitch + x;
2015-08-02 18:00:00 +02:00
2017-12-06 17:00:00 +01:00
for (int j = 0; j < h; ++j) {
for (int i = 0; i < w; ++i) {
p[i] = _rgbPalette[buf[i]];
2015-08-02 18:00:00 +02:00
}
2017-12-06 17:00:00 +01:00
p += _screenW;
buf += pitch;
}
if (_pi.dbgMask & PlayerInput::DF_DBLOCKS) {
drawRect(x, y, w, h, 0xE7);
2015-08-02 18:00:00 +02:00
}
}
void SystemStub_SDL::fadeScreen() {
_fadeOnUpdateScreen = true;
}
void SystemStub_SDL::updateScreen(int shakeOffset) {
2017-06-07 18:00:00 +02:00
if (_texW != _screenW || _texH != _screenH) {
void *dst = 0;
int pitch = 0;
if (SDL_LockTexture(_texture, 0, &dst, &pitch) == 0) {
2017-11-03 17:00:00 +01:00
assert((pitch & 3) == 0);
_scaler->scale(_scaleFactor, (uint32_t *)dst, pitch / sizeof(uint32_t), _screenBuffer, _screenW, _screenW, _screenH);
2017-06-07 18:00:00 +02:00
SDL_UnlockTexture(_texture);
}
} else {
2017-11-03 17:00:00 +01:00
SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t));
2017-06-07 18:00:00 +02:00
}
2016-05-09 18:00:00 +02:00
SDL_RenderClear(_renderer);
2016-08-07 18:00:00 +02:00
if (_fadeOnUpdateScreen) {
SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
SDL_Rect r;
r.x = r.y = 0;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
for (int i = 1; i <= 16; ++i) {
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 256 - i * 16);
SDL_RenderCopy(_renderer, _texture, 0, 0);
SDL_RenderFillRect(_renderer, &r);
SDL_RenderPresent(_renderer);
SDL_Delay(30);
}
_fadeOnUpdateScreen = false;
2017-11-03 17:00:00 +01:00
SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_NONE);
2016-08-07 18:00:00 +02:00
return;
}
2016-05-09 18:00:00 +02:00
if (shakeOffset != 0) {
SDL_Rect r;
r.x = 0;
2017-11-03 17:00:00 +01:00
r.y = shakeOffset * _scaleFactor;
2016-05-09 18:00:00 +02:00
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
r.h -= r.y;
SDL_RenderCopy(_renderer, _texture, 0, &r);
} else {
SDL_RenderCopy(_renderer, _texture, 0, 0);
}
SDL_RenderPresent(_renderer);
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::processEvents() {
2016-03-20 17:00:00 +01:00
bool paused = false;
2015-08-02 18:00:00 +02:00
while (true) {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
processEvent(ev, paused);
if (_pi.quit) {
return;
}
}
if (!paused) {
break;
}
SDL_Delay(100);
}
}
void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
2017-11-03 17:00:00 +01:00
switch (ev.type) {
case SDL_QUIT:
_pi.quit = true;
break;
2016-05-09 18:00:00 +02:00
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_FOCUS_LOST:
paused = (ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST);
SDL_PauseAudio(paused);
break;
}
break;
case SDL_JOYHATMOTION:
if (_joystick) {
2015-08-02 18:00:00 +02:00
_pi.dirMask = 0;
if (ev.jhat.value & SDL_HAT_UP) {
_pi.dirMask |= PlayerInput::DIR_UP;
}
if (ev.jhat.value & SDL_HAT_DOWN) {
_pi.dirMask |= PlayerInput::DIR_DOWN;
}
if (ev.jhat.value & SDL_HAT_LEFT) {
_pi.dirMask |= PlayerInput::DIR_LEFT;
}
if (ev.jhat.value & SDL_HAT_RIGHT) {
_pi.dirMask |= PlayerInput::DIR_RIGHT;
}
2016-05-09 18:00:00 +02:00
}
break;
case SDL_JOYAXISMOTION:
if (_joystick) {
2015-08-02 18:00:00 +02:00
switch (ev.jaxis.axis) {
case 0:
2016-05-09 18:00:00 +02:00
_pi.dirMask &= ~(PlayerInput::DIR_RIGHT | PlayerInput::DIR_LEFT);
if (ev.jaxis.value > kJoystickCommitValue) {
2015-08-02 18:00:00 +02:00
_pi.dirMask |= PlayerInput::DIR_RIGHT;
2016-05-09 18:00:00 +02:00
} else if (ev.jaxis.value < -kJoystickCommitValue) {
2015-08-02 18:00:00 +02:00
_pi.dirMask |= PlayerInput::DIR_LEFT;
}
break;
case 1:
2016-05-09 18:00:00 +02:00
_pi.dirMask &= ~(PlayerInput::DIR_UP | PlayerInput::DIR_DOWN);
if (ev.jaxis.value > kJoystickCommitValue) {
2015-08-02 18:00:00 +02:00
_pi.dirMask |= PlayerInput::DIR_DOWN;
2016-05-09 18:00:00 +02:00
} else if (ev.jaxis.value < -kJoystickCommitValue) {
2015-08-02 18:00:00 +02:00
_pi.dirMask |= PlayerInput::DIR_UP;
}
break;
}
2016-05-09 18:00:00 +02:00
}
break;
case SDL_JOYBUTTONDOWN:
2016-08-07 18:00:00 +02:00
case SDL_JOYBUTTONUP:
2016-05-09 18:00:00 +02:00
if (_joystick) {
2016-08-07 18:00:00 +02:00
const bool pressed = (ev.jbutton.state == SDL_PRESSED);
2015-08-02 18:00:00 +02:00
switch (ev.jbutton.button) {
case 0:
2016-08-07 18:00:00 +02:00
_pi.space = pressed;
2015-08-02 18:00:00 +02:00
break;
case 1:
2016-08-07 18:00:00 +02:00
_pi.shift = pressed;
2015-08-02 18:00:00 +02:00
break;
case 2:
2016-08-07 18:00:00 +02:00
_pi.enter = pressed;
2015-08-02 18:00:00 +02:00
break;
case 3:
2016-08-07 18:00:00 +02:00
_pi.backspace = pressed;
2015-08-02 18:00:00 +02:00
break;
}
2016-05-09 18:00:00 +02:00
}
break;
2016-08-07 18:00:00 +02:00
case SDL_CONTROLLERAXISMOTION:
if (_controller) {
switch (ev.caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX:
case SDL_CONTROLLER_AXIS_RIGHTX:
if (ev.caxis.value < -kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_LEFT;
} else {
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
}
if (ev.caxis.value > kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_RIGHT;
} else {
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
}
2015-08-02 18:00:00 +02:00
break;
2016-08-07 18:00:00 +02:00
case SDL_CONTROLLER_AXIS_LEFTY:
case SDL_CONTROLLER_AXIS_RIGHTY:
if (ev.caxis.value < -kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_UP;
} else {
_pi.dirMask &= ~PlayerInput::DIR_UP;
}
if (ev.caxis.value > kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_DOWN;
} else {
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
}
2015-08-02 18:00:00 +02:00
break;
2016-08-07 18:00:00 +02:00
}
}
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
if (_controller) {
const bool pressed = (ev.cbutton.state == SDL_PRESSED);
switch (ev.cbutton.button) {
case SDL_CONTROLLER_BUTTON_A:
_pi.enter = pressed;
2015-08-02 18:00:00 +02:00
break;
2016-08-07 18:00:00 +02:00
case SDL_CONTROLLER_BUTTON_B:
_pi.space = pressed;
break;
case SDL_CONTROLLER_BUTTON_X:
_pi.shift = pressed;
break;
case SDL_CONTROLLER_BUTTON_Y:
_pi.backspace = pressed;
break;
case SDL_CONTROLLER_BUTTON_BACK:
case SDL_CONTROLLER_BUTTON_START:
_pi.escape = pressed;
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
if (pressed) {
_pi.dirMask |= PlayerInput::DIR_UP;
} else {
_pi.dirMask &= ~PlayerInput::DIR_UP;
}
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
if (pressed) {
_pi.dirMask |= PlayerInput::DIR_DOWN;
} else {
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
}
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
if (pressed) {
_pi.dirMask |= PlayerInput::DIR_LEFT;
} else {
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
}
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
if (pressed) {
_pi.dirMask |= PlayerInput::DIR_RIGHT;
} else {
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
}
2015-08-02 18:00:00 +02:00
break;
}
2016-05-09 18:00:00 +02:00
}
break;
2017-11-03 17:00:00 +01:00
case SDL_KEYUP:
if (ev.key.keysym.mod & KMOD_ALT) {
2015-08-02 18:00:00 +02:00
switch (ev.key.keysym.sym) {
case SDLK_RETURN:
2017-11-03 17:00:00 +01:00
changeGraphics(!_fullscreen, _scaleFactor);
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_KP_PLUS:
case SDLK_PAGEUP:
changeGraphics(_fullscreen, _scaleFactor + 1);
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_KP_MINUS:
case SDLK_PAGEDOWN:
changeGraphics(_fullscreen, _scaleFactor - 1);
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_s: {
2016-03-20 17:00:00 +01:00
char name[32];
2017-11-03 17:00:00 +01:00
snprintf(name, sizeof(name), "screenshot-%03d.tga", _screenshot);
saveTGA(name, (const uint8_t *)_screenBuffer, _screenW, _screenH);
2016-03-20 17:00:00 +01:00
++_screenshot;
debug(DBG_INFO, "Written '%s'", name);
2015-08-02 18:00:00 +02:00
}
break;
2017-11-03 17:00:00 +01:00
case SDLK_x:
_pi.quit = true;
break;
2015-08-02 18:00:00 +02:00
}
2017-11-03 17:00:00 +01:00
break;
} else if (ev.key.keysym.mod & KMOD_CTRL) {
2015-08-02 18:00:00 +02:00
switch (ev.key.keysym.sym) {
2017-11-03 17:00:00 +01:00
case SDLK_f:
_pi.dbgMask ^= PlayerInput::DF_FASTMODE;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_b:
_pi.dbgMask ^= PlayerInput::DF_DBLOCKS;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_i:
_pi.dbgMask ^= PlayerInput::DF_SETLIFE;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_s:
_pi.save = true;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_l:
_pi.load = true;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_KP_PLUS:
case SDLK_PAGEUP:
_pi.stateSlot = 1;
2015-08-02 18:00:00 +02:00
break;
2017-11-03 17:00:00 +01:00
case SDLK_KP_MINUS:
case SDLK_PAGEDOWN:
_pi.stateSlot = -1;
2015-08-02 18:00:00 +02:00
break;
}
break;
2017-11-03 17:00:00 +01:00
}
_pi.lastChar = ev.key.keysym.sym;
switch (ev.key.keysym.sym) {
case SDLK_LEFT:
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
break;
case SDLK_RIGHT:
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
break;
case SDLK_UP:
_pi.dirMask &= ~PlayerInput::DIR_UP;
break;
case SDLK_DOWN:
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
break;
case SDLK_SPACE:
_pi.space = false;
break;
case SDLK_RSHIFT:
case SDLK_LSHIFT:
_pi.shift = false;
break;
case SDLK_RETURN:
_pi.enter = false;
break;
case SDLK_ESCAPE:
_pi.escape = false;
break;
2015-08-02 18:00:00 +02:00
default:
break;
}
2017-11-03 17:00:00 +01:00
break;
case SDL_KEYDOWN:
if (ev.key.keysym.mod & (KMOD_ALT | KMOD_CTRL)) {
2017-12-06 17:00:00 +01:00
break;
2017-11-03 17:00:00 +01:00
}
switch (ev.key.keysym.sym) {
case SDLK_LEFT:
_pi.dirMask |= PlayerInput::DIR_LEFT;
break;
case SDLK_RIGHT:
_pi.dirMask |= PlayerInput::DIR_RIGHT;
break;
case SDLK_UP:
_pi.dirMask |= PlayerInput::DIR_UP;
break;
case SDLK_DOWN:
_pi.dirMask |= PlayerInput::DIR_DOWN;
break;
case SDLK_BACKSPACE:
case SDLK_TAB:
_pi.backspace = true;
break;
case SDLK_SPACE:
_pi.space = true;
break;
case SDLK_RSHIFT:
case SDLK_LSHIFT:
_pi.shift = true;
break;
case SDLK_RETURN:
_pi.enter = true;
break;
case SDLK_ESCAPE:
_pi.escape = true;
break;
default:
break;
}
break;
default:
break;
}
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::sleep(int duration) {
SDL_Delay(duration);
}
uint32_t SystemStub_SDL::getTimeStamp() {
return SDL_GetTicks();
}
2016-05-09 18:00:00 +02:00
static void mixAudioS16(void *param, uint8_t *buf, int len) {
2015-08-02 18:00:00 +02:00
SystemStub_SDL *stub = (SystemStub_SDL *)param;
2016-05-09 18:00:00 +02:00
memset(buf, 0, len);
stub->_audioCbProc(stub->_audioCbData, (int16_t *)buf, len / 2);
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::startAudio(AudioCallback callback, void *param) {
2017-12-06 17:00:00 +01:00
SDL_AudioSpec desired;
2015-08-02 18:00:00 +02:00
memset(&desired, 0, sizeof(desired));
2016-05-09 18:00:00 +02:00
desired.freq = kAudioHz;
desired.format = AUDIO_S16SYS;
2015-08-02 18:00:00 +02:00
desired.channels = 1;
desired.samples = 2048;
2016-05-09 18:00:00 +02:00
desired.callback = mixAudioS16;
2015-08-02 18:00:00 +02:00
desired.userdata = this;
2017-12-06 17:00:00 +01:00
if (SDL_OpenAudio(&desired, 0) == 0) {
2015-08-02 18:00:00 +02:00
_audioCbProc = callback;
_audioCbData = param;
SDL_PauseAudio(0);
} else {
error("SystemStub_SDL::startAudio() Unable to open sound device");
}
}
void SystemStub_SDL::stopAudio() {
SDL_CloseAudio();
}
uint32_t SystemStub_SDL::getOutputSampleRate() {
2016-05-09 18:00:00 +02:00
return kAudioHz;
2015-08-02 18:00:00 +02:00
}
void SystemStub_SDL::lockAudio() {
SDL_LockAudio();
}
void SystemStub_SDL::unlockAudio() {
SDL_UnlockAudio();
}
2016-05-09 18:00:00 +02:00
void SystemStub_SDL::prepareGraphics() {
2017-11-03 17:00:00 +01:00
_texW = _screenW;
_texH = _screenH;
switch (_scalerType) {
case kScalerTypePoint:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling
break;
case kScalerTypeLinear:
2016-05-09 18:00:00 +02:00
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
break;
2017-11-03 17:00:00 +01:00
case kScalerTypeInternal:
case kScalerTypeExternal:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
_texW *= _scaleFactor;
_texH *= _scaleFactor;
2016-05-09 18:00:00 +02:00
break;
}
2017-11-03 17:00:00 +01:00
const int windowW = _screenW * _scaleFactor;
const int windowH = _screenH * _scaleFactor;
2016-05-09 18:00:00 +02:00
int flags = 0;
if (_fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
2018-01-14 17:00:00 +01:00
} else {
flags |= SDL_WINDOW_RESIZABLE;
2015-08-02 18:00:00 +02:00
}
2016-05-09 18:00:00 +02:00
_window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags);
2017-11-03 17:00:00 +01:00
SDL_Surface *icon = SDL_LoadBMP(kIconBmp);
if (icon) {
SDL_SetWindowIcon(_window, icon);
SDL_FreeSurface(icon);
}
2016-05-09 18:00:00 +02:00
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(_renderer, windowW, windowH);
2017-06-07 18:00:00 +02:00
_texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH);
2016-05-09 18:00:00 +02:00
_fmt = SDL_AllocFormat(kPixelFormat);
2015-08-02 18:00:00 +02:00
}
2016-05-09 18:00:00 +02:00
void SystemStub_SDL::cleanupGraphics() {
2015-08-02 18:00:00 +02:00
if (_screenBuffer) {
free(_screenBuffer);
_screenBuffer = 0;
}
2016-05-09 18:00:00 +02:00
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
2017-12-06 17:00:00 +01:00
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
2016-05-09 18:00:00 +02:00
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
2015-08-02 18:00:00 +02:00
}
2017-11-03 17:00:00 +01:00
void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
2017-12-06 17:00:00 +01:00
int factor = scaleFactor;
if (factor < _scaler->factorMin) {
factor = _scaler->factorMin;
} else if (factor > _scaler->factorMax) {
factor = _scaler->factorMax;
}
if (fullscreen == _fullscreen && factor == _scaleFactor) {
// no change
return;
2016-05-09 18:00:00 +02:00
}
2017-12-06 17:00:00 +01:00
_fullscreen = fullscreen;
_scaleFactor = factor;
2016-05-09 18:00:00 +02:00
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
2017-12-06 17:00:00 +01:00
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
2016-05-09 18:00:00 +02:00
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
2017-11-03 17:00:00 +01:00
prepareGraphics();
2015-08-02 18:00:00 +02:00
}
2017-12-06 17:00:00 +01:00
void SystemStub_SDL::drawRect(int x, int y, int w, int h, uint8_t color) {
const int x1 = x;
const int y1 = y;
const int x2 = x + w - 1;
const int y2 = y + h - 1;
2015-08-02 18:00:00 +02:00
assert(x1 >= 0 && x2 < _screenW && y1 >= 0 && y2 < _screenH);
for (int i = x1; i <= x2; ++i) {
2017-11-03 17:00:00 +01:00
*(_screenBuffer + y1 * _screenW + i) = *(_screenBuffer + y2 * _screenW + i) = _rgbPalette[color];
2015-08-02 18:00:00 +02:00
}
for (int j = y1; j <= y2; ++j) {
2017-11-03 17:00:00 +01:00
*(_screenBuffer + j * _screenW + x1) = *(_screenBuffer + j * _screenW + x2) = _rgbPalette[color];
2015-08-02 18:00:00 +02:00
}
}