blues/sys_sdl2.c

286 lines
6.7 KiB
C

#include <SDL.h>
#include "sys.h"
#include "util.h"
#define COPPER_BARS_H 80
static const int SCALE = 2;
static const int FADE_STEPS = 16;
static int _screen_w;
static int _screen_h;
static SDL_Window *_window;
static SDL_Renderer *_renderer;
static SDL_Texture *_texture;
static SDL_PixelFormat *_fmt;
static uint32_t _screen_palette[32];
static uint32_t *_screen_buffer;
static struct input_t *_input = &g_sys.input;
static int _copper_color;
static uint32_t _copper_palette[COPPER_BARS_H];
static int sdl2_init() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_ShowCursor(SDL_DISABLE);
_screen_w = _screen_h = 0;
_window = 0;
_renderer = 0;
_texture = 0;
_fmt = 0;
memset(_screen_palette, 0, sizeof(_screen_palette));
_screen_buffer = 0;
_copper_color = -1;
return 0;
}
static void sdl2_fini() {
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
free(_screen_buffer);
SDL_Quit();
}
static void sdl2_set_screen_size(int w, int h, const char *caption) {
assert(_screen_w == 0 && _screen_h == 0); // abort if called more than once
_screen_w = w;
_screen_h = h;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* nearest pixel sampling */
const int window_w = w * SCALE;
const int window_h = h * SCALE; // * 4 / 3;
_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, 0);
_renderer = SDL_CreateRenderer(_window, -1, 0);
print_debug(DBG_SYSTEM, "set_screen_size %d,%d", _screen_w, _screen_h);
_screen_buffer = (uint32_t *)calloc(_screen_w * _screen_h, sizeof(uint32_t));
if (!_screen_buffer) {
print_error("Failed to allocate screen buffer");
}
static const uint32_t pfmt = SDL_PIXELFORMAT_RGB888;
_texture = SDL_CreateTexture(_renderer, pfmt, SDL_TEXTUREACCESS_STREAMING, _screen_w, _screen_h);
_fmt = SDL_AllocFormat(pfmt);
}
static uint32_t convert_amiga_color(uint16_t color) {
uint8_t r = (color >> 8) & 15;
r |= r << 4;
uint8_t g = (color >> 4) & 15;
g |= g << 4;
uint8_t b = color & 15;
b |= b << 4;
return SDL_MapRGB(_fmt, r, g, b);
}
static void sdl2_set_palette_amiga(const uint16_t *colors) {
for (int i = 0; i < 32; ++i) {
_screen_palette[i] = convert_amiga_color(colors[i]);
}
}
static void sdl2_set_copper_bars(const uint16_t *data) {
if (!data) {
_copper_color = -1;
} else {
_copper_color = (data[0] - 0x180) / 2;
const uint16_t *src = data + 1;
uint32_t *dst = _copper_palette;
for (int i = 0; i < 16; ++i) {
const int j = i + 1;
*dst++ = convert_amiga_color(src[j]);
*dst++ = convert_amiga_color(src[i]);
*dst++ = convert_amiga_color(src[j]);
*dst++ = convert_amiga_color(src[i]);
*dst++ = convert_amiga_color(src[j]);
}
}
}
static void sdl2_set_screen_palette(const uint8_t *colors, int count) {
for (int i = 0; i < count; ++i) {
_screen_palette[i] = SDL_MapRGB(_fmt, colors[0], colors[1], colors[2]);
colors += 3;
}
}
static void fade_palette_helper(int in) {
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 <= FADE_STEPS; ++i) {
int alpha = 256 * i / FADE_STEPS;
if (in) {
alpha = 256 - alpha;
}
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, alpha);
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _texture, 0, 0);
SDL_RenderFillRect(_renderer, &r);
SDL_RenderPresent(_renderer);
SDL_Delay(30);
}
}
static void sdl2_fade_in_palette() {
fade_palette_helper(1);
}
static void sdl2_fade_out_palette() {
fade_palette_helper(0);
}
static void sdl2_update_screen(const uint8_t *p, int present) {
if (_copper_color != -1) {
for (int j = 0; j < _screen_h; ++j) {
for (int i = 0; i < _screen_w; ++i) {
if (*p == _copper_color && j / 2 < COPPER_BARS_H) {
_screen_buffer[j * _screen_w + i] = _copper_palette[j / 2];
} else {
_screen_buffer[j * _screen_w + i] = _screen_palette[*p];
}
++p;
}
}
} else {
for (int i = 0; i < _screen_w * _screen_h; ++i) {
_screen_buffer[i] = _screen_palette[p[i]];
}
}
SDL_UpdateTexture(_texture, 0, _screen_buffer, _screen_w * sizeof(uint32_t));
if (present) {
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _texture, 0, 0);
SDL_RenderPresent(_renderer);
}
}
static void handle_keyevent(int keysym, int keydown) {
switch (keysym) {
case SDLK_LEFT:
if (keydown) {
_input->direction |= INPUT_DIRECTION_LEFT;
} else {
_input->direction &= ~INPUT_DIRECTION_LEFT;
}
break;
case SDLK_RIGHT:
if (keydown) {
_input->direction |= INPUT_DIRECTION_RIGHT;
} else {
_input->direction &= ~INPUT_DIRECTION_RIGHT;
}
break;
case SDLK_UP:
if (keydown) {
_input->direction |= INPUT_DIRECTION_UP;
} else {
_input->direction &= ~INPUT_DIRECTION_UP;
}
break;
case SDLK_DOWN:
if (keydown) {
_input->direction |= INPUT_DIRECTION_DOWN;
} else {
_input->direction &= ~INPUT_DIRECTION_DOWN;
}
break;
case SDLK_RETURN:
case SDLK_SPACE:
_input->space = keydown;
break;
}
}
static int handle_event(const SDL_Event *ev) {
switch (ev->type) {
case SDL_QUIT:
_input->quit = 1;
break;
case SDL_KEYUP:
handle_keyevent(ev->key.keysym.sym, 0);
break;
case SDL_KEYDOWN:
handle_keyevent(ev->key.keysym.sym, 1);
break;
default:
return -1;
}
return 0;
}
static void sdl2_process_events() {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
handle_event(&ev);
if (_input->quit) {
break;
}
}
}
static void sdl2_sleep(int duration) {
SDL_Delay(duration);
}
static uint32_t sdl2_get_timestamp() {
return SDL_GetTicks();
}
static void sdl2_start_audio(sys_audio_cb callback, void *param) {
SDL_AudioSpec desired;
memset(&desired, 0, sizeof(desired));
desired.freq = SYS_AUDIO_FREQ;
desired.format = AUDIO_S16;
desired.channels = 1;
desired.samples = 2048;
desired.callback = callback;
desired.userdata = param;
if (SDL_OpenAudio(&desired, 0) == 0) {
SDL_PauseAudio(0);
}
}
static void sdl2_stop_audio() {
SDL_CloseAudio();
}
static void sdl2_lock_audio() {
SDL_LockAudio();
}
static void sdl2_unlock_audio() {
SDL_UnlockAudio();
}
struct sys_t g_sys = {
.init = sdl2_init,
.fini = sdl2_fini,
.set_screen_size = sdl2_set_screen_size,
.set_screen_palette = sdl2_set_screen_palette,
.set_palette_amiga = sdl2_set_palette_amiga,
.set_copper_bars = sdl2_set_copper_bars,
.fade_in_palette = sdl2_fade_in_palette,
.fade_out_palette = sdl2_fade_out_palette,
.update_screen = sdl2_update_screen,
.process_events = sdl2_process_events,
.sleep = sdl2_sleep,
.get_timestamp = sdl2_get_timestamp,
.start_audio = sdl2_start_audio,
.stop_audio = sdl2_stop_audio,
.lock_audio = sdl2_lock_audio,
.unlock_audio = sdl2_unlock_audio,
};