Merge branch 'sdl3': migrate from SDL 1.2 to SDL 3

Brings the rendering, audio, input, and joystick layers from SDL 1.2
era APIs to native SDL 3, removes the ~2300-line glSDL OpenGL shim
that is now obsolete, and strips all the dead SDL 1.2 video-surface
state machinery (page flipping, hardware-surface flags, depth/driver
selection).

Major changes:
- Build system: find_package(SDL3 CONFIG) + SDL3::SDL3 /
  SDL3_image::SDL3_image. sdl12-compat is no longer needed.
- Renderer: SDL_CreateWindow + SDL_CreateRenderer + a streaming
  SDL_Texture replaces SDL_SetVideoMode + SDL_Flip. The CPU-side
  back-buffer (softbuf) is now always ARGB8888 and uploaded once per
  frame. SDL_SetRenderLogicalPresentation handles letterbox scaling.
- glSDL shim deleted (graphics/glSDL.{c,h}, ~2300 lines).
- Surface API: SDL_FreeSurface→SDL_DestroySurface, SDL_FillRect→
  SDL_FillSurfaceRect, SDL_DisplayFormat→SDL_ConvertSurface(ARGB8888),
  SDL_CreateRGBSurface→SDL_CreateSurface, SDL_SetColorKey/SetAlpha→
  SDL_SetSurfaceColorKey/AlphaMod, surface->format access via
  SDL_GetPixelFormatDetails.
- Events: SDL_KEYDOWN→SDL_EVENT_KEY_DOWN etc., ev.key.keysym.sym→
  ev.key.key, ev.button.x/y for mouse buttons, SDL_VIDEOEXPOSE→
  SDL_EVENT_WINDOW_EXPOSED, SDL_ACTIVEEVENT→SDL_EVENT_WINDOW_FOCUS_LOST.
- Keyboard: SDLKey→SDL_Keycode, SDLK_KP1..9→SDLK_KP_1..9, lowercase
  SDLK_p/y/n→uppercase, KMOD_META→SDL_KMOD_GUI.
- Mouse grab: SDL_WM_GrabInput→SDL_SetWindowMouseGrab (via new
  gfxengine_t::window_handle() accessor).
- Text input: SDL_EnableUNICODE→SDL_StartTextInput/SDL_StopTextInput
  with SDL_EVENT_TEXT_INPUT forwarded to gsm.press(-1, ch).
- Joystick: SDL_NumJoysticks/JoystickOpen→SDL_GetJoysticks (instance
  IDs)/SDL_OpenJoystick/SDL_CloseJoystick.
- Audio: SDL_OpenAudio + push-callback model→SDL_OpenAudioDeviceStream
  + SDL_AudioStreamCallback (pull). The engine's existing
  _audio_callback is kept; an adapter (sdl3_stream_callback) runs it
  on a scratch buffer and pushes the result via SDL_PutAudioStreamData.
- AUDIO_S16SYS→SDL_AUDIO_S16, SDL_FreeWAV→SDL_free.

Cleanup:
- Removed gfxengine_t fields _doublebuf, _pages, _shadow, _depth,
  _driver, xflags, broken_rgba8, backpage/frontpage and the per-page
  dirty-rect arrays (collapsed to 1-D).
- Removed prefs fields videodriver, depth, doublebuf, shadow, pages,
  broken_rgba8. Existing config files are still readable (silently
  absorbed as obsolete keys).
- Removed the Display Depth / Buffering Mode / Software Shadow Buffer
  / Display Buffer Pages / Broken RGBA8 entries from the options
  menu.
- Removed the doublebuffer()-guarded second pass of the loading-
  animation nibble draw in dashboard.cpp.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ville Lindholm 2026-05-28 22:13:39 +03:00
commit 76c0776aec
No known key found for this signature in database
GPG Key ID: 89AE9EAA3B6FDE7C
31 changed files with 582 additions and 3472 deletions

View File

@ -35,11 +35,9 @@ check_symbol_exists(_vsnprintf stdio.h HAVE__VSNPRINTF)
# ---------------------------------------------------------------------------
# Third-party libraries
# ---------------------------------------------------------------------------
# This codebase targets SDL 1.2 and SDL_image 1.2. CMake's bundled FindSDL
# / FindSDL_image modules locate these directly; on macOS with Homebrew,
# `brew install sdl12-compat sdl_image` works.
find_package(SDL REQUIRED)
find_package(SDL_image REQUIRED)
# Migrating to SDL 3. Homebrew: `brew install sdl3 sdl3_image`.
find_package(SDL3 CONFIG REQUIRED)
find_package(SDL3_image CONFIG REQUIRED)
if(KOBO_ENABLE_OPENGL)
find_package(OpenGL)
@ -122,9 +120,8 @@ target_compile_definitions(kobo_deps INTERFACE
target_include_directories(kobo_deps INTERFACE
${CMAKE_CURRENT_BINARY_DIR} # for generated aconfig.h
${CMAKE_CURRENT_SOURCE_DIR} # for config.h
${SDL_INCLUDE_DIR}
)
target_link_libraries(kobo_deps INTERFACE ${SDL_LIBRARY} ${SDL_IMAGE_LIBRARIES})
target_link_libraries(kobo_deps INTERFACE SDL3::SDL3 SDL3_image::SDL3_image)
if(HAVE_OPENGL)
target_compile_definitions(kobo_deps INTERFACE HAVE_OPENGL)

View File

@ -309,8 +309,8 @@ void dashboard_window_t::nibble(int tool)
}
gengine->invalidate();
gengine->flip();
if(gengine->doublebuffer())
{
// SDL 3: one logical back buffer; previously a second pass was
// only needed when actual hardware double-buffering was in use.
for(i = 0; i < dt * 4; ++i)
{
if(last_index >= NIBBLE_TILES)
@ -337,7 +337,6 @@ void dashboard_window_t::nibble(int tool)
++last_index;
}
}
}
mode(DASHBOARD_BLACK);
gengine->flip();
}

View File

@ -54,7 +54,10 @@ void gamecontrol_t::init(int always_fire)
down = 0;
shot = 0;
movekey_pressed = 0;
SDL_EnableKeyRepeat(r_delay, r_interval);
// SDL 3 removed SDL_EnableKeyRepeat; OS-level key repeat fires
// SDL_EVENT_KEY_DOWN with ev.key.repeat set. r_delay/r_interval
// retained in case we re-implement software repeat later.
(void)r_delay; (void)r_interval;
}
@ -68,9 +71,8 @@ void gamecontrol_t::repeat(int delay, int interval)
{
r_delay = delay;
r_interval = interval;
//Temporary kludge - should apply repeat to
//all switch inputs, not just the keyboard!
SDL_EnableKeyRepeat(delay, interval);
// SDL_EnableKeyRepeat is gone in SDL 3. Stored for future
// software-side repeat handling.
}
@ -80,7 +82,7 @@ void gamecontrol_t::clear()
}
int gamecontrol_t::map(SDLKey sym)
int gamecontrol_t::map(SDL_Keycode sym)
{
/*
FIXME: This should be replaced by a configurable mapping system.
@ -137,7 +139,7 @@ FIXME: This should be replaced by a configurable mapping system.
case SDLK_ESCAPE:
return BTN_EXIT;
case SDLK_PAUSE:
case SDLK_p:
case SDLK_P:
return BTN_PAUSE;
case SDLK_SPACE:
return BTN_START;
@ -148,9 +150,9 @@ FIXME: This should be replaced by a configurable mapping system.
return BTN_INC;
case SDLK_KP_MINUS:
return BTN_DEC;
case SDLK_y:
case SDLK_Y:
return BTN_YES;
case SDLK_n:
case SDLK_N:
return BTN_NO;
case SDLK_BACKSPACE:
return BTN_BACK;

View File

@ -23,17 +23,17 @@
#ifndef _KOBO_GAMECTL_H_
#define _KOBO_GAMECTL_H_
#include "glSDL.h"
#include "sdl_compat.h"
#include "config.h"
#define KEY_KP_DOWN SDLK_KP2
#define KEY_KP_LEFT SDLK_KP4
#define KEY_KP_UP SDLK_KP8
#define KEY_KP_RIGHT SDLK_KP6
#define KEY_KP_DL SDLK_KP1
#define KEY_KP_DR SDLK_KP3
#define KEY_KP_UL SDLK_KP7
#define KEY_KP_UR SDLK_KP9
#define KEY_KP_DOWN SDLK_KP_2
#define KEY_KP_LEFT SDLK_KP_4
#define KEY_KP_UP SDLK_KP_8
#define KEY_KP_RIGHT SDLK_KP_6
#define KEY_KP_DL SDLK_KP_1
#define KEY_KP_DR SDLK_KP_3
#define KEY_KP_UL SDLK_KP_7
#define KEY_KP_UR SDLK_KP_9
enum buttons_t
@ -105,7 +105,7 @@ class gamecontrol_t
static void init(int always_fire);
static void repeat(int delay, int interval);
static void clear();
static int map(SDLKey sym);
static int map(SDL_Keycode sym);
static void process(); // Call every frame!
static void press(int k);
static void release(int k);

View File

@ -2,7 +2,6 @@ add_library(graphics STATIC
cs.c
sprite.c
filters.c
glSDL.c
gfxengine.cpp
window.cpp
sofont.cpp

View File

@ -22,8 +22,8 @@
#include <string.h>
#include <math.h>
#include "logger.h"
#include "glSDL.h"
#include "SDL_image.h"
#include "sdl_compat.h"
#include <SDL3_image/SDL_image.h>
#include "sprite.h"
#include "filters.h"
@ -235,33 +235,17 @@ int s_filter_rgba8(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
unsigned i;
SDL_PixelFormat fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.BitsPerPixel = 32;
fmt.BytesPerPixel = 4;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
fmt.Rmask = 0xff000000;
fmt.Gmask = 0x00ff0000;
fmt.Bmask = 0x0000ff00;
fmt.Amask = 0x000000ff;
#else
fmt.Rmask = 0x000000ff;
fmt.Gmask = 0x0000ff00;
fmt.Bmask = 0x00ff0000;
fmt.Amask = 0xff000000;
#endif
for(i = 0; i < frames; ++i)
{
SDL_Surface *tmp;
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
tmp = SDL_ConvertSurface(s->surface, &fmt,
SDL_SWSURFACE);
tmp = SDL_ConvertSurface(s->surface, SDL_PIXELFORMAT_RGBA32);
if(!tmp)
return -1;
SDL_FreeSurface(s->surface);
SDL_DestroySurface(s->surface);
s->surface = tmp;
}
return 0;
@ -289,7 +273,7 @@ int s_filter_dither(s_bank_t *b, unsigned first, unsigned frames,
switch (s_blitmode)
{
case S_BLITMODE_AUTO:
if(s->surface->format->Amask)
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
ar = ag = ab = 8;
break;
case S_BLITMODE_OPAQUE:
@ -458,7 +442,8 @@ int s_filter_dither(s_bank_t *b, unsigned first, unsigned frames,
static void tweak_ck(SDL_Surface *s)
{
int x, y;
if(s->format->BitsPerPixel != 32)
const SDL_PixelFormatDetails *fmt = SDL_GetPixelFormatDetails(s->format);
if(!fmt || fmt->bits_per_pixel != 32)
return;
for(y = 0; y < s->h; ++y)
{
@ -488,53 +473,54 @@ int s_filter_displayformat(s_bank_t * b, unsigned first, unsigned frames,
switch(s_blitmode)
{
case S_BLITMODE_AUTO:
if(s->surface->format->Amask)
SDL_SetAlpha(s->surface,
SDL_SRCALPHA |
SDL_RLEACCEL,
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
{
SDL_SetSurfaceAlphaMod(s->surface,
SDL_ALPHA_OPAQUE);
SDL_SetSurfaceBlendMode(s->surface,
SDL_BLENDMODE_BLEND);
}
else
SDL_SetColorKey(s->surface, SDL_RLEACCEL, 0);
SDL_SetSurfaceColorKey(s->surface, false, 0);
break;
case S_BLITMODE_OPAQUE:
SDL_SetColorKey(s->surface, SDL_RLEACCEL, 0);
SDL_SetSurfaceColorKey(s->surface, false, 0);
break;
case S_BLITMODE_COLORKEY:
SDL_SetColorKey(s->surface,
SDL_SRCCOLORKEY | SDL_RLEACCEL,
SDL_MapRGB(s->surface->format,
SDL_SetSurfaceColorKey(s->surface, true,
SDL_MapSurfaceRGB(s->surface,
s_colorkey.r,
s_colorkey.g,
s_colorkey.b));
break;
case S_BLITMODE_ALPHA:
SDL_SetAlpha(s->surface,
SDL_SRCALPHA | SDL_RLEACCEL,
s_alpha);
SDL_SetSurfaceAlphaMod(s->surface, s_alpha);
SDL_SetSurfaceBlendMode(s->surface,
SDL_BLENDMODE_BLEND);
break;
}
// SDL 3 dropped SDL_DisplayFormat. We always convert sprites
// to the back-buffer format (ARGB8888); see gfxengine.cpp.
if(args->x)
{
if(s->surface->format->Amask)
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
tweak_ck(s->surface);
tmp = SDL_DisplayFormat(s->surface);
if(s->surface->format->Amask)
SDL_SetColorKey(tmp,
SDL_SRCCOLORKEY | SDL_RLEACCEL,
SDL_MapRGB(tmp->format, 0,0,0));
tmp = SDL_ConvertSurface(s->surface,
SDL_PIXELFORMAT_ARGB8888);
if(tmp && SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
SDL_SetSurfaceColorKey(tmp, true,
SDL_MapSurfaceRGB(tmp, 0,0,0));
}
else
{
if(s->surface->format->Amask)
tmp = SDL_DisplayFormatAlpha(s->surface);
else
tmp = SDL_DisplayFormat(s->surface);
tmp = SDL_ConvertSurface(s->surface,
SDL_PIXELFORMAT_ARGB8888);
}
if(!tmp)
return -1;
SDL_FreeSurface(s->surface);
SDL_DestroySurface(s->surface);
s->surface = tmp;
}
return 0;
@ -900,18 +886,13 @@ int s_filter_scale(s_bank_t *b, unsigned first, unsigned frames,
if(!s)
continue;
params.src = s->surface;
params.dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
params.max_x, params.max_y, 32,
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
#else
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
#endif
params.dst = SDL_CreateSurface(params.max_x, params.max_y,
SDL_PIXELFORMAT_RGBA32);
if(!params.dst)
return -1;
#ifdef DEBUG
SDL_FillRect(params.dst, NULL,
SDL_MapRGB(params.dst->format, 255, 128, 0));
SDL_FillSurfaceRect(params.dst, NULL,
SDL_MapSurfaceRGB(params.dst, 255, 128, 0));
#endif
/* Deal with SFont marker row */
@ -957,7 +938,7 @@ int s_filter_scale(s_bank_t *b, unsigned first, unsigned frames,
break;
}
SDL_FreeSurface(s->surface);
SDL_DestroySurface(s->surface);
s->surface = params.dst;
}
b->w = params.max_x;

View File

@ -28,8 +28,8 @@
#include "gfxengine.h"
#include "filters.h"
#include "SDL_image.h"
#include "glSDL.h"
#include <SDL3_image/SDL_image.h>
#include "sdl_compat.h"
#include "sofont.h"
#include "window.h"
@ -40,6 +40,9 @@ gfxengine_t::gfxengine_t()
{
gfxengine = this; /* Uurgh! Kludge. */
sdl_window = NULL;
sdl_renderer = NULL;
fb_texture = NULL;
screen_surface = NULL;
softbuf = NULL;
fullwin = NULL;
@ -51,15 +54,10 @@ gfxengine_t::gfxengine_t()
sf1 = df = dsf = acf = NULL;
gfx = NULL;
csengine = NULL;
_driver = GFX_DRIVER_SDL2D;
_shadow = 1;
_doublebuf = 1;
_pages = -1;
_vsync = 1;
_fullscreen = 0;
_centered = 0;
use_interpolation = 1;
_depth = 0;
_title = "GfxEngine v0.4";
_icontitle = "GfxEngine";
_cursor = 1;
@ -68,11 +66,9 @@ gfxengine_t::gfxengine_t()
_autoinvalidate = 1;
_scalemode = GFX_SCALE_NEAREST;
_clamping = 0;
xflags = 0;
_dither = 0;
_dither_type = 0;
broken_rgba8 = 0;
alpha_threshold = 0;
_brightness = 1.0;
@ -91,10 +87,7 @@ gfxengine_t::gfxengine_t()
for(int i = 0; i < CS_LAYERS ; ++i)
xratio[i] = yratio[i] = 0.0;
dirtyrects[0] = 0;
dirtyrects[1] = 0;
frontpage = 0;
backpage = 1;
dirtyrects = 0;
screenshot_count = 0;
}
@ -157,75 +150,23 @@ void gfxengine_t::scale(float x, float y)
void gfxengine_t::mode(int bits, int fullscreen)
{
// SDL 3 uses the renderer's pixel format directly; the legacy depth
// argument is accepted for API compatibility but ignored.
(void)bits;
int was_showing = is_showing;
hide();
int olddepth = _depth;
_depth = bits;
if(_depth != olddepth)
reload();
_fullscreen = fullscreen;
if(was_showing)
show();
}
void gfxengine_t::driver(gfx_drivers_t drv)
{
int was_showing = is_showing;
hide();
_driver = drv;
if(was_showing)
show();
}
void gfxengine_t::doublebuffer(int use)
{
if(_doublebuf == use)
return;
int was_showing = is_showing;
hide();
_doublebuf = use;
if(was_showing)
show();
}
void gfxengine_t::pages(int np)
{
_pages = np;
}
void gfxengine_t::vsync(int use)
{
if(_vsync == use)
return;
int was_showing = is_showing;
hide();
_vsync = use;
if(was_showing)
show();
}
void gfxengine_t::shadow(int use)
{
if(_shadow == use)
return;
int was_showing = is_showing;
hide();
_shadow = use;
if(was_showing)
show();
if(sdl_renderer)
SDL_SetRenderVSync(sdl_renderer, use ? 1 : 0);
}
void gfxengine_t::autoinvalidate(int use)
@ -432,9 +373,9 @@ void gfxengine_t::clampcolor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
void gfxengine_t::dither(int type, int _broken_rgba8)
{
(void)_broken_rgba8; // legacy OpenGL workaround; no-op in SDL 3
_dither = type >= 0;
_dither_type = type;
broken_rgba8 = _broken_rgba8;
if(!df)
return;
@ -443,23 +384,13 @@ void gfxengine_t::dither(int type, int _broken_rgba8)
if(_dither)
{
if(_driver == GFX_DRIVER_GLSDL)
{
//Klugde for glSDL, which doesn't give us a faked
//screen surface - while we're interested only in
//the *texture* depth; not the display depth!
df->args.r = df->args.g = df->args.b = 0;
//Another kludge, because some cards support RGB8
//(24 bit) textures, but not RGBA8 (32 bit).
df->args.x = broken_rgba8;
}
else
{
// SDL 3: back buffer is always ARGB8888 (8 bits per channel,
// no loss). The legacy formula was 1 << (Rloss - 1); for 8 bpc
// the dithering amplitude collapses to 0.
df->args.x = 0;
df->args.r = 1<<(screen_surface->format->Rloss-1);
df->args.g = 1<<(screen_surface->format->Gloss-1);
df->args.b = 1<<(screen_surface->format->Bloss-1);
}
df->args.r = 0;
df->args.g = 0;
df->args.b = 0;
}
else
df->args.x = df->args.r = df->args.g = df->args.b = 0;
@ -685,10 +616,8 @@ void gfxengine_t::on_frame(cs_engine_t *e)
}
int gfxengine_t::open(int objects, int extraflags)
int gfxengine_t::open(int objects)
{
xflags = extraflags;
if(is_open)
return show();
@ -745,8 +674,8 @@ void gfxengine_t::title(const char *win, const char *icon)
{
_title = win;
_icontitle = icon;
if(screen_surface)
SDL_WM_SetCaption(_title, _icontitle);
if(sdl_window)
SDL_SetWindowTitle(sdl_window, _title);
}
@ -756,131 +685,94 @@ void gfxengine_t::title(const char *win, const char *icon)
int gfxengine_t::show()
{
int flags = 0;
if(!is_open)
return -1;
if(is_showing)
return 0;
if(_centered && !_fullscreen)
SDL_putenv((char *)"SDL_VIDEO_CENTERED=1");
log_printf(DLOG, "Opening screen...\n");
if(!SDL_WasInit(SDL_INIT_VIDEO))
if(SDL_InitSubSystem(SDL_INIT_VIDEO) == -1)
if(!SDL_InitSubSystem(SDL_INIT_VIDEO))
{
log_printf(ELOG, "Failed to initialize SDL!\n");
log_printf(ELOG, "Failed to initialize SDL: %s\n",
SDL_GetError());
return -2;
}
switch(_driver)
{
case GFX_DRIVER_SDL2D:
break;
case GFX_DRIVER_GLSDL:
if(!_doublebuf)
{
log_printf(WLOG, "Only double buffering is supported"
" with OpenGL drivers!\n");
doublebuffer(1);
}
if(_shadow)
{
log_printf(WLOG, "Shadow buffer not supported"
" with OpenGL drivers!\n");
shadow(0);
}
break;
}
switch(_driver)
{
case GFX_DRIVER_SDL2D:
/* Nothing extra */
break;
case GFX_DRIVER_GLSDL:
flags |= SDL_GLSDL;
break;
}
if(_doublebuf)
flags |= SDL_DOUBLEBUF | SDL_HWSURFACE;
else
{
if(!_shadow)
flags |= SDL_HWSURFACE;
}
// SDL 3 window + renderer model.
//
// The kobo engine renders into a CPU-side back buffer (softbuf) via
// SDL_BlitSurface/SDL_FillSurfaceRect, and uploads the result to a
// streaming SDL_Texture once per frame. This preserves the entire
// software-compositing code path inherited from SDL 1.2 (window.cpp,
// sprite.c, filters.c, sofont.cpp), while letting the GPU handle
// scaling and presentation. SDL_DOUBLEBUF / SDL_HWSURFACE / page-
// flipping flags are obsolete and ignored.
Uint64 win_flags = 0;
if(_fullscreen)
flags |= SDL_FULLSCREEN;
win_flags |= SDL_WINDOW_FULLSCREEN;
glSDL_VSync(_vsync);
flags |= xflags;
screen_surface = SDL_SetVideoMode(_width, _height, _depth, flags);
if(!screen_surface)
sdl_window = SDL_CreateWindow(_title ? _title : "Kobo Deluxe",
_width, _height, win_flags);
if(!sdl_window)
{
log_printf(ELOG, "Failed to open display!\n");
log_printf(ELOG, "Failed to open window: %s\n", SDL_GetError());
return -3;
}
if(_driver != GFX_DRIVER_GLSDL)
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if(!sdl_renderer)
{
if((screen_surface->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF)
{
if(!_doublebuf)
{
log_printf(WLOG, "Could not get"
" single buffered display.\n");
doublebuffer(1);
}
}
else
{
if(_doublebuf)
{
log_printf(WLOG, "Could not get"
" double buffered display.\n");
doublebuffer(0);
}
}
log_printf(ELOG, "Failed to create renderer: %s\n",
SDL_GetError());
SDL_DestroyWindow(sdl_window);
sdl_window = NULL;
return -4;
}
if((screen_surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE)
{
if(_shadow)
{
softbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
_width, _height,
screen_surface->format->BitsPerPixel,
screen_surface->format->Rmask,
screen_surface->format->Gmask,
screen_surface->format->Bmask,
screen_surface->format->Amask);
// Letterbox the back buffer when the window is resized / fullscreen.
SDL_SetRenderLogicalPresentation(sdl_renderer, _width, _height,
SDL_LOGICAL_PRESENTATION_LETTERBOX);
SDL_SetRenderVSync(sdl_renderer, _vsync ? 1 : 0);
// The back buffer is always a CPU-side ARGB8888 surface.
softbuf = SDL_CreateSurface(_width, _height, SDL_PIXELFORMAT_ARGB8888);
if(!softbuf)
{
log_printf(WLOG, "Failed to create shadow buffer! "
"Trying direct rendering.\n");
shadow(0);
}
}
}
else
{
if(_shadow)
log_printf(WLOG, "Shadow buffer requested; "
"relying on SDL's shadow buffer.\n");
else
{
log_printf(WLOG, "Could not get h/w display surface.\n");
shadow(0); //...which means we're using SDL's shadow.
}
log_printf(ELOG, "Failed to create back buffer: %s\n",
SDL_GetError());
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
sdl_renderer = NULL;
sdl_window = NULL;
return -5;
}
screen_surface = softbuf; // alias for legacy code paths
fb_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING, _width, _height);
if(!fb_texture)
{
log_printf(ELOG, "Failed to create framebuffer texture: %s\n",
SDL_GetError());
SDL_DestroySurface(softbuf);
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
softbuf = screen_surface = NULL;
sdl_renderer = NULL;
sdl_window = NULL;
return -6;
}
SDL_SetTextureScaleMode(fb_texture,
(_scalemode == GFX_SCALE_NEAREST) ?
SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
if(_cursor)
SDL_ShowCursor();
else
SDL_HideCursor();
SDL_WM_SetCaption(_title, _icontitle);
SDL_ShowCursor(_cursor);
cs_engine_set_size(csengine, _width, _height);
csengine->filter = use_interpolation;
@ -933,12 +825,27 @@ void gfxengine_t::hide(void)
w = w->next;
}
if(fb_texture)
{
SDL_DestroyTexture(fb_texture);
fb_texture = NULL;
}
if(softbuf)
{
SDL_FreeSurface(softbuf);
SDL_DestroySurface(softbuf);
softbuf = NULL;
}
screen_surface = NULL;
if(sdl_renderer)
{
SDL_DestroyRenderer(sdl_renderer);
sdl_renderer = NULL;
}
if(sdl_window)
{
SDL_DestroyWindow(sdl_window);
sdl_window = NULL;
}
is_showing = 0;
}
@ -946,54 +853,37 @@ void gfxengine_t::hide(void)
void gfxengine_t::invalidate(SDL_Rect *rect, window_t *window)
{
switch(_pages)
{
case -1:
if(_doublebuf)
__invalidate(1, rect, window);
__invalidate(0, rect, window);
break;
case 0:
__invalidate(0, NULL, NULL);
break;
case 3:
__invalidate(2, rect, window);
// Fallthrough!
case 2:
__invalidate(1, rect, window);
// Fallthrough!
case 1:
__invalidate(0, rect, window);
break;
}
__invalidate(rect, window);
}
void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
void gfxengine_t::__invalidate(SDL_Rect *rect, window_t *window)
{
if(!screen_surface)
return;
if(!rect || (_pages == 0))
if(!rect)
{
dirtyrects[page] = 1;
dirtytable[page][0].x = 0;
dirtytable[page][0].y = 0;
dirtytable[page][0].w = screen_surface->w;
dirtytable[page][0].h = screen_surface->h;
dirtywtable[page][0] = NULL;
dirtyrects = 1;
dirtytable[0].x = 0;
dirtytable[0].y = 0;
dirtytable[0].w = screen_surface->w;
dirtytable[0].h = screen_surface->h;
dirtywtable[0] = NULL;
return;
}
/* Clip to screen (stolen from SDL_surface.c) */
SDL_Rect dr = *rect;
SDL_Rect screen_clip;
SDL_GetSurfaceClipRect(screen_surface, &screen_clip);
int Amin, Amax, Bmin, Bmax;
/* Horizontal intersection */
Amin = dr.x;
Amax = Amin + dr.w;
Bmin = screen_surface->clip_rect.x;
Bmax = Bmin + screen_surface->clip_rect.w;
Bmin = screen_clip.x;
Bmax = Bmin + screen_clip.w;
if(Bmin > Amin)
Amin = Bmin;
dr.x = Amin;
@ -1004,8 +894,8 @@ void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
/* Vertical intersection */
Amin = dr.y;
Amax = Amin + dr.h;
Bmin = screen_surface->clip_rect.y;
Bmax = Bmin + screen_surface->clip_rect.h;
Bmin = screen_clip.y;
Bmax = Bmin + screen_clip.h;
if(Bmin > Amin)
Amin = Bmin;
dr.y = Amin;
@ -1016,19 +906,18 @@ void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
if(!dr.w || !dr.h)
return;
for(int i = 0; i < dirtyrects[page]; ++i)
if(memcmp(&dirtytable[page][i], &dr, sizeof(dr)) == 0)
for(int i = 0; i < dirtyrects; ++i)
if(memcmp(&dirtytable[i], &dr, sizeof(dr)) == 0)
return;
if(dirtyrects[page] < MAX_DIRTYRECTS - 1)
if(dirtyrects < MAX_DIRTYRECTS - 1)
{
dirtytable[page][dirtyrects[page]] = dr;
dirtywtable[page][dirtyrects[page]] = window;
++dirtyrects[page];
dirtytable[dirtyrects] = dr;
dirtywtable[dirtyrects] = window;
++dirtyrects;
}
else
log_printf(ELOG, "gfxengine: Page %d out of dirtyrects!\n",
page);
log_printf(ELOG, "gfxengine: Out of dirtyrects!\n");
}
@ -1109,7 +998,12 @@ void gfxengine_t::cursor(int csr)
{
_cursor = csr;
if(screen_surface)
SDL_ShowCursor(csr);
{
if(csr)
SDL_ShowCursor();
else
SDL_HideCursor();
}
}
@ -1215,8 +1109,8 @@ void gfxengine_t::frame()
SDL_Event ev;
while(SDL_PollEvent(&ev))
{
if(ev.type == SDL_KEYDOWN)
if(ev.key.keysym.sym == SDLK_ESCAPE)
if(ev.type == SDL_EVENT_KEY_DOWN)
if(ev.key.key == SDLK_ESCAPE)
stop();
}
}
@ -1283,82 +1177,31 @@ void gfxengine_t::refresh_rect(SDL_Rect *r)
void gfxengine_t::flip()
{
if(!screen_surface)
if(!screen_surface || !sdl_renderer || !fb_texture)
return;
// Init dirtyrect table flipping, if necessary.
switch(_pages)
// SDL 3 has no hardware page flipping; we always present one back
// buffer per frame. Keep the dirtyrect iteration only to drive the
// phys_refresh() callbacks that window widgets rely on; the upload
// is a single full-surface copy regardless.
for(int i = 0; i < dirtyrects; ++i)
{
case -1:
if(!_doublebuf)
frontpage = backpage = 0;
else if(frontpage == backpage)
if(dirtywtable[i])
{
frontpage = 0;
backpage = 1;
}
break;
case 0:
frontpage = backpage = 0;
invalidate();
break;
case 1:
frontpage = backpage = 0;
break;
case 2:
case 3:
if(frontpage == backpage)
{
frontpage = 0;
backpage = 1;
}
break;
}
// Process the dirtyrects.
int i;
for(i = 0; i < dirtyrects[backpage]; ++i)
{
if(dirtywtable[backpage][i])
{
if(!dirtywtable[backpage][i]->visible())
if(!dirtywtable[i]->visible())
continue;
SDL_Rect dr = dirtytable[backpage][i];
dirtywtable[backpage][i]->phys_refresh(&dr);
SDL_Rect dr = dirtytable[i];
dirtywtable[i]->phys_refresh(&dr);
}
else
refresh_rect(&dirtytable[backpage][i]);
refresh_rect(&dirtytable[i]);
}
dirtyrects = 0;
// Perform the actual flip or update
if(_shadow)
{
for(i = 0; i < dirtyrects[backpage]; ++i)
SDL_BlitSurface(softbuf,
&dirtytable[backpage][i],
screen_surface,
&dirtytable[backpage][i]);
}
if(_doublebuf)
{
dirtyrects[backpage] = 0;
if(_pages == -1)
{
backpage = !backpage;
frontpage = !frontpage;
}
else if(_pages > 1)
{
backpage = (backpage + 1) % _pages;
frontpage = (frontpage + 1) % _pages;
}
SDL_Flip(screen_surface);
}
else
{
SDL_UpdateRects(screen_surface, dirtyrects[0], dirtytable[0]);
dirtyrects[0] = 0;
}
SDL_UpdateTexture(fb_texture, NULL, softbuf->pixels, softbuf->pitch);
SDL_RenderClear(sdl_renderer);
SDL_RenderTexture(sdl_renderer, fb_texture, NULL, NULL);
SDL_RenderPresent(sdl_renderer);
}

View File

@ -24,21 +24,14 @@
#define GFX_BANKS 256
#define MAX_DIRTYRECTS 1024
#define MAX_PAGES 3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "glSDL.h"
#include "sdl_compat.h"
#include "sprite.h"
#include "cs.h"
enum gfx_drivers_t
{
GFX_DRIVER_SDL2D = 0,
GFX_DRIVER_GLSDL = 1
};
enum gfx_scalemodes_t
{
GFX_SCALE_NEAREST = 0,
@ -70,25 +63,11 @@ class gfxengine_t
void size(int w, int h);
void centered(int c);
void scale(float x, float y);
void driver(gfx_drivers_t drv);
void mode(int bits, int fullscreen);
// 1: Use double buffering if possible
void doublebuffer(int use);
// -1: Use default for shadow() and doublebuffer() settings
// 0: None; assume flipping gives you a garbage buffer
// 1: Assume flipping leaves the back buffer intact
// 2: Assume two buffers that are swapped when flipping
// 3: Assume three buffers cycled when flipping
void pages(int np);
// 1: Enable vsync, if available
void vsync(int use);
// 1: Use a software shadow back buffer, if possible
void shadow(int use);
void autoinvalidate(int use);
void interpolation(int inter);
@ -103,12 +82,10 @@ class gfxengine_t
void wrap(int x, int y);
/* Info */
int doublebuffer() { return _doublebuf; }
int shadow() { return _shadow; }
int autoinvalidate() { return _autoinvalidate; }
/* Engine open/close */
int open(int objects = 1024, int extraflags = 0);
int open(int objects = 1024);
void close();
/* Data management (use while engine is open) */
@ -172,6 +149,7 @@ class gfxengine_t
/* Control */
cs_engine_t *cs() { return csengine; }
SDL_Window *window_handle() { return sdl_window; }
SDL_Surface *surface();
void flip(); // Flip pages
void update(); // Full update, making current draw page visible
@ -206,16 +184,16 @@ class gfxengine_t
float yscale() { return ys * (1.f/256.f); }
protected:
gfx_drivers_t _driver;
gfx_scalemodes_t _scalemode;
int _clamping;
SDL_Surface *screen_surface;
SDL_Window *sdl_window;
SDL_Renderer *sdl_renderer;
SDL_Texture *fb_texture;
SDL_Surface *screen_surface; // alias for softbuf in SDL 3
SDL_Surface *softbuf;
int backpage;
int frontpage;
int dirtyrects[MAX_PAGES];
SDL_Rect dirtytable[MAX_PAGES][MAX_DIRTYRECTS];
window_t *dirtywtable[MAX_PAGES][MAX_DIRTYRECTS];
int dirtyrects;
SDL_Rect dirtytable[MAX_DIRTYRECTS];
window_t *dirtywtable[MAX_DIRTYRECTS];
window_t *fullwin;
window_t *window;
window_t *windows; // Linked list
@ -233,23 +211,17 @@ class gfxengine_t
s_container_t *gfx;
SoFont *fonts[GFX_BANKS]; // Kludge.
cs_engine_t *csengine;
int xflags;
int _doublebuf;
int _pages;
int _vsync;
int _shadow;
int _fullscreen;
int _centered;
int _autoinvalidate;
int use_interpolation;
int _width, _height;
int _depth;
const char *_title;
const char *_icontitle;
int _cursor;
int _dither;
int _dither_type;
int broken_rgba8; //Klugde for OpenGL (if RGBA8 ==> RGBA4)
int alpha_threshold; //For noalpha()
float _brightness;
float _contrast;
@ -264,8 +236,7 @@ class gfxengine_t
int screenshot_count;
void __invalidate(int page, SDL_Rect *rect = NULL,
window_t *window = NULL);
void __invalidate(SDL_Rect *rect = NULL, window_t *window = NULL);
void refresh_rect(SDL_Rect *r);
static void on_frame(cs_engine_t *e);

File diff suppressed because it is too large Load Diff

View File

@ -1,399 +0,0 @@
/*(LGPL)
------------------------------------------------------------
glSDL 0.9 - SDL 2D API on top of OpenGL
------------------------------------------------------------
* Copyright (C) 2001-2004, 2006-2007 David Olofson
* This code is released under the terms of the GNU LGPL.
*/
#ifndef _GLSDL_H_
#define _GLSDL_H_
/*
* If you don't use GNU autotools or similar, uncomment this to
* compile with OpenGL enabled:
#define HAVE_OPENGL
*
* NOTE:
* See README about using this glSDL wrapper with
* SDL versions that have the glSDL backend!
*/
/* We're still using SDL datatypes here - we just add some stuff. */
#include "SDL.h"
/*
* Ignore the flag from SDL w/ glSDL backend, since we're going
* to use glSDL/wrapper by default.
*/
#ifdef SDL_GLSDL
#undef SDL_GLSDL
#endif
#ifndef HAVE_OPENGL
/* Fakes to make glSDL code compile with SDL. */
#define SDL_GLSDL 0
#define GLSDL_FIX_SURFACE(s)
#else /* HAVE_OPENGL */
#ifdef __cplusplus
extern "C" {
#endif
#if (SDL_MAJOR_VERSION <= 1) && (SDL_MINOR_VERSION <= 2) && \
(SDL_PATCHLEVEL < 5)
#warning glSDL: Using SDL version 1.2.5 or later is strongly recommended!
#endif
/*----------------------------------------------------------
SDL style API
----------------------------------------------------------*/
typedef enum glSDL_TileModes
{
GLSDL_TM_SINGLE,
GLSDL_TM_HORIZONTAL,
GLSDL_TM_VERTICAL,
GLSDL_TM_HUGE
} glSDL_TileModes;
typedef struct glSDL_TexInfo
{
int textures;
int *texture;
int texsize; /* width/height of OpenGL texture */
glSDL_TileModes tilemode;
int tilew, tileh; /* At least one must equal texsize! */
int tilespertex;
/* Area of surface to download when/after unlocking */
SDL_Rect invalid_area;
} glSDL_TexInfo;
#define GLSDL_FIX_SURFACE(s) (s)->unused1 = 0;
#define IS_GLSDL_SURFACE(s) ((s) && texinfotab && glSDL_GetTexInfo(s))
#ifdef SDL_GLSDL
#undef SDL_GLSDL /* In case SDL has the glSDL backend... */
#endif
#define SDL_GLSDL 0x00100000 /* Create an OpenGL 2D rendering context */
/*
* Wraps SDL_SetVideoMode(), and adds support for the SDL_GLSDL flag.
*
* If 'flags' contains SDL_GLSDL, glSDL_SetVideoMode() sets up a "pure"
* OpenGL rendering context for use with the glSDL_ calls.
*
* SDL can be closed as usual (using SDL_ calls), but you should call
* glSDL_Quit() (kludge) to allow glSDL to clean up it's internal stuff.
*/
SDL_Surface *glSDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
void glSDL_Quit(void);
void glSDL_QuitSubSystem(Uint32 flags);
/* Replaces SDL_Quit() entirely, when using the override defines */
void glSDL_FullQuit(void);
SDL_Surface *glSDL_GetVideoSurface(void);
void glSDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
void glSDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h);
/*
* Works like SDL_Flip(), but may also perform enqueued blits.
* (That is, it's possible that the implementation renders
* *nothing* until glSDL_Flip() is called.)
*/
int glSDL_Flip(SDL_Surface *screen);
void glSDL_FreeSurface(SDL_Surface *surface);
int glSDL_LockSurface(SDL_Surface *surface);
void glSDL_UnlockSurface(SDL_Surface *surface);
/*
* Like the respective SDL functions, although they ignore
* SDL_RLEACCEL, as it makes no sense in this context.
*/
int glSDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);
int glSDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);
/*
* Sets up clipping for the screen, or a SDL_Surface.
*
* Note that this function takes both SDL_Surfaces and
* glSDL_Surfaces.
*/
SDL_bool glSDL_SetClipRect(SDL_Surface *surface, SDL_Rect *rect);
int glSDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect);
int glSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color);
/*
* Convert the given surface into a SDL_Surface (if it isn't
* one already), and makes sure that the underlying SDL_Surface
* is of a pixel format suitable for fast texture downloading.
*
* Note that you *only* have to use this function if you want
* fast pixel access to surfaces (ie "procedural textures").
* Any surfaces that aren't converted will be downloaded
* automatically upon the first call to glSDL_BlitSurface(),
* but if conversion is required, it will be required for
* every glSDL_UnlockSurface() call.
*
* IMPORTANT:
* You *can* pass an SDL_Surface directly to this function,
* and it will try to deal with it nicely. However, this
* requires that a temporary SDL_Surface is created, and
* this surface is cached only until the texture memory is
* needed for new surfaces.
*/
SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface);
SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface);
SDL_Surface *glSDL_ConvertSurface
(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags);
SDL_Surface *glSDL_CreateRGBSurface
(Uint32 flags, int width, int height, int depth,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);
SDL_Surface *glSDL_CreateRGBSurfaceFrom(void *pixels,
int width, int height, int depth, int pitch,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);
SDL_Surface *glSDL_LoadBMP(const char *file);
int glSDL_SaveBMP(SDL_Surface *surface, const char *file);
#ifdef __cplusplus
}
#endif
/* Some ugly "overriding"... */
#ifndef _GLSDL_NO_REDEFINES_
/*
* You *always* need to lock and unlock a glSDL surface in
* order to get glSDL to update the OpenGL texture!
*/
#undef SDL_MUSTLOCK
#define SDL_MUSTLOCK(surface) \
(surface->offset || \
((surface->flags & (SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_RLEACCEL)) != 0) || \
IS_GLSDL_SURFACE(surface))
#define SDL_SetVideoMode glSDL_SetVideoMode
#define SDL_GetVideoSurface glSDL_GetVideoSurface
#define SDL_Quit glSDL_FullQuit
#define SDL_QuitSubSystem glSDL_QuitSubSystem
#define SDL_UpdateRects glSDL_UpdateRects
#define SDL_UpdateRect glSDL_UpdateRect
#define SDL_Flip glSDL_Flip
#define SDL_FreeSurface glSDL_FreeSurface
#define SDL_LockSurface glSDL_LockSurface
#define SDL_UnlockSurface glSDL_UnlockSurface
#define SDL_SetColorKey glSDL_SetColorKey
#define SDL_SetAlpha glSDL_SetAlpha
#define SDL_SetClipRect glSDL_SetClipRect
#undef SDL_BlitSurface
#define SDL_BlitSurface glSDL_BlitSurface
#define SDL_FillRect glSDL_FillRect
#define SDL_DisplayFormat glSDL_DisplayFormat
#define SDL_DisplayFormatAlpha glSDL_DisplayFormatAlpha
#define SDL_ConvertSurface glSDL_ConvertSurface
#define SDL_CreateRGBSurface glSDL_CreateRGBSurface
#define SDL_CreateRGBSurfaceFrom glSDL_CreateRGBSurfaceFrom
#undef SDL_AllocSurface
#define SDL_AllocSurface glSDL_CreateRGBSurface
#undef SDL_LoadBMP
#define SDL_LoadBMP glSDL_LoadBMP
#undef SDL_SaveBMP
#define SDL_SaveBMP glSDL_SaveBMP
#define IMG_Load(x) glSDL_IMG_Load(x)
#endif
#endif /* HAVE_OPENGL */
#ifdef HAVE_OPENGL
/* Some extra overloading for common external lib calls... */
#include "SDL_image.h"
#ifdef __cplusplus
extern "C" {
#endif
SDL_Surface *glSDL_IMG_Load(const char *file);
#ifdef __cplusplus
}
#endif
#endif /* HAVE_OPENGL */
/*----------------------------------------------------------
glSDL API extensions, transparent
----------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif
/*
* Invalidate part of a texture.
*
* This function can be used either between calls to
* glSDL_LockSurface() and glSDL_UnlockSurface(), or before
* calling glSDL_DownloadSurface().
*
* In either case, it causes only the specified area to be
* downloaded when unlocking the surface, or calling
* glSDL_UnlockSurface(), respectively.
*
* Note that if this function is not used, glSDL assumes that
* the entire surface has to be updated. (That is, it's safe
* to ignore this function - it's "just a performance hack.")
*
* Passing a rectangle with zero height or width cancels the
* downloading when/after unlocking the surface. Use if you
* just want to read the texture, but feel like being nice and
* obeying SDL_MUSTLOCK() - which is a good idea, as things
* may change...
*
* Passing NULL for the 'area' argument results in the entire
* surface being invalidated.
*
* NOTE: This function does NOT perform clipping! Weird or
* even Bad Things may happen if you specify areas
* that protrude outside the edges of the actual
* surface.
*/
void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area);
/*
* Make sure that the texture of the specified surface is up
* to date in OpenGL texture memory.
*
* This can be used together with glSDL_UnloadSurface() to
* implement custom texture caching schemes.
*
* Returns 0 on success, or a negative value if something
* went wrong.
*/
int glSDL_UploadSurface(SDL_Surface *surface);
/*
* Free the texture space used by the specified surface.
*
* Normally, glSDL should download textures when needed, and
* unload the oldest (in terms of use) surfaces, if it runs out
* of texture space.
*/
void glSDL_UnloadSurface(SDL_Surface *surface);
/*----------------------------------------------------------
glSDL API extensions, OpenGL mode ONLY
----------------------------------------------------------*/
/*
* Enable, disable or check the state of "vsync" or
* vertical retrace synchronization. Note that enabling
* this does not *guarantee* that the driver actually
* uses vsync; it only instructs OpenGL to do so if
* possible.
* Pass 1 for 'use' to enable vsync, '0' to disable
* it, and -1 to just return the current state without
* changing it.
* Returns the vsync state *before* any change made
* by the call.
*
* NOTE: Ignored when not using OpenGL acceleration!
*/
int glSDL_VSync(int use);
/*
* Set alpha multiplier. This is multiplied with any full
* surface alpha or alpha channels of surfaces.
*
* The default value is 255, which makes glSDL render like
* the standard SDL 2D backends, ie based only on the
* surface settings.
*
* This function affects the global state of glSDL.
*
* NOTE: NOT available without OpenGL acceleration!
*/
void glSDL_SetBlendAlpha(Uint8 alpha);
/*
* Set color multipliers. These are multiplied with the
* respective color channers of rendered surfaces,
* modulating the brightness of the resulting output.
*
* The default values are 255, which makes glSDL render
* like the standard SDL 2D backends.
*
* This function affects the global state of glSDL.
*
* NOTE: NOT available without OpenGL acceleration!
*/
void glSDL_SetBlendColor(Uint8 r, Uint8 g, Uint8 b);
/*
* Set center offset in pixels for scaling and rotation.
*
* The default is (0, 0), which makes surfaces rotate and
* scale around their centers.
*
* This function affects the global state of glSDL.
*
* NOTE: NOT available without OpenGL acceleration!
*/
void glSDL_SetCenter(float x, float y);
/*
* Set rotation angle in degrees. Rotation is done around
* the center point, which is defined as
* (w/2 + cx, h/2 + cy)
* where w and h are the width and height of the source
* surface, and cx and cy are the center offsets set by
* glSDL_SetCenter().
*
* The default value is 0.0f, which makes glSDL render
* like a normal SDL 2D backend.
*
* This function affects the global state of glSDL.
*
* NOTE: NOT available without OpenGL acceleration!
*/
void glSDL_SetRotation(float angle);
/*
* Set scale factor. Scaling is centered around around
* the point
* (w/2 + cx, h/2 + cy)
* where w and h are the width and height of the source
* surface, and cx and cy are the center offsets set by
* glSDL_SetCenter().
*
* The default value is 1.0f, which makes glSDL render
* like a normal SDL 2D backend.
*
* This function affects the global state of glSDL.
*
* NOTE: NOT available without OpenGL acceleration!
*/
void glSDL_SetScale(float x, float y);
/*
* Reset glSDL global state to defaults.
*/
void glSDL_ResetState(void);
#ifdef __cplusplus
}
#endif
#endif /* _GLSDL_H_ */

View File

@ -44,7 +44,9 @@ static Uint32 gp2(SDL_Surface *surface, int x, int y)
static Uint32 gp3(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
const SDL_PixelFormatDetails *fmt =
SDL_GetPixelFormatDetails(surface->format);
int bpp = fmt ? fmt->bytes_per_pixel : 3;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
@ -74,7 +76,14 @@ RGN_region *RGN_ScanMask(SDL_Surface *src, Uint32 key)
RGN_FreeRegion(rgn);
return NULL;
}
switch(src->format->BytesPerPixel)
const SDL_PixelFormatDetails *src_fmt =
SDL_GetPixelFormatDetails(src->format);
if(!src_fmt)
{
RGN_FreeRegion(rgn);
return NULL;
}
switch(src_fmt->bytes_per_pixel)
{
case 1:
gp = gp1;

View File

@ -22,7 +22,7 @@
#ifndef KOBO_REGION_H
#define KOBO_REGION_H
#include "glSDL.h"
#include "sdl_compat.h"
#ifdef __cplusplus
extern "C" {

16
graphics/sdl_compat.h Normal file
View File

@ -0,0 +1,16 @@
/*
* sdl_compat.h - umbrella SDL 3 include for Kobo Deluxe.
*
* Replaces the legacy graphics/glSDL.h shim that wrapped SDL 1.2 with
* an OpenGL backend. SDL 3's renderer makes that unnecessary.
*
* This header exists so the codebase can use #include "sdl_compat.h"
* rather than scatter <SDL3/SDL.h> across the tree (which would also
* make a later SDL 4 swap painful).
*/
#ifndef _KOBO_SDL_COMPAT_H_
#define _KOBO_SDL_COMPAT_H_
#include <SDL3/SDL.h>
#endif /* _KOBO_SDL_COMPAT_H_ */

View File

@ -20,7 +20,7 @@ SoFont::SoFont()
SoFont::~SoFont()
{
if(picture)
SDL_FreeSurface(picture);
SDL_DestroySurface(picture);
delete[]CharPos;
delete[]CharOffset;
delete[]Spacing;
@ -45,7 +45,11 @@ namespace SoFontUtilities
return 0;
}
Bpp = Surface->format->BytesPerPixel;
const SDL_PixelFormatDetails *fmt =
SDL_GetPixelFormatDetails(Surface->format);
if(!fmt)
return 0;
Bpp = fmt->bytes_per_pixel;
bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch +
X * Bpp;
@ -63,10 +67,10 @@ namespace SoFontUtilities
case 3:
// Format/endian independent
Uint8 r, g, b;
r = *((bits) + Surface->format->Rshift / 8);
g = *((bits) + Surface->format->Gshift / 8);
b = *((bits) + Surface->format->Bshift / 8);
return SDL_MapRGB(Surface->format, r, g, b);
r = *((bits) + fmt->Rshift / 8);
g = *((bits) + fmt->Gshift / 8);
b = *((bits) + fmt->Bshift / 8);
return SDL_MapSurfaceRGB(Surface, r, g, b);
break;
case 4:
return *((Uint32 *) Surface->pixels +
@ -94,7 +98,11 @@ namespace SoFontUtilities
return;
}
Bpp = Surface->format->BytesPerPixel;
const SDL_PixelFormatDetails *fmt =
SDL_GetPixelFormatDetails(Surface->format);
if(!fmt)
return;
Bpp = fmt->bytes_per_pixel;
bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch +
X * Bpp;
@ -113,10 +121,11 @@ namespace SoFontUtilities
case 3:
// Format/endian independent
Uint8 r, g, b;
SDL_GetRGB(c, Surface->format, &r, &g, &b);
*((bits) + Surface->format->Rshift / 8) = r;
*((bits) + Surface->format->Gshift / 8) = g;
*((bits) + Surface->format->Bshift / 8) = b;
SDL_GetRGB(c, fmt, SDL_GetSurfacePalette(Surface),
&r, &g, &b);
*((bits) + fmt->Rshift / 8) = r;
*((bits) + fmt->Gshift / 8) = g;
*((bits) + fmt->Bshift / 8) = b;
break;
case 4:
*((Uint32 *) Surface->pixels +
@ -224,7 +233,7 @@ bool SoFont::DoStartNewChar(Sint32 x)
if(!picture)
return false;
return SoFontGetPixel(picture, x, 0) ==
SDL_MapRGB(picture->format, 255, 0, 255);
SDL_MapSurfaceRGB(picture, 255, 0, 255);
}
void SoFont::CleanSurface()
@ -233,7 +242,7 @@ void SoFont::CleanSurface()
return;
int x = 0, y = 0;
Uint32 pix = SDL_MapRGB(picture->format, 255, 0, 255);
Uint32 pix = SDL_MapSurfaceRGB(picture, 255, 0, 255);
while(x < picture->w)
{
@ -259,7 +268,7 @@ bool SoFont::load(SDL_Surface * FontSurface)
return false;
}
if(picture)
SDL_FreeSurface(picture);
SDL_DestroySurface(picture);
picture = FontSurface;
height = picture->h - 1;
while(x < picture->w)
@ -499,8 +508,10 @@ void SoFont::CenteredString(SDL_Surface * Surface, const char *text,
{
if(!picture)
return;
CenteredString(Surface, Surface->clip_rect.w / 2,
Surface->clip_rect.h / 2, text, clip);
SDL_Rect surf_clip;
SDL_GetSurfaceClipRect(Surface, &surf_clip);
CenteredString(Surface, surf_clip.w / 2,
surf_clip.h / 2, text, clip);
}
void SoFont::PutStringCleverCursor(SDL_Surface * Surface, const char *text,

View File

@ -34,7 +34,7 @@
* Cursor tests first check if '|' is present.
* Shadowed variables fixed.
* Garbage data in spacing table fixed. (Thanks to
Andreas Spångberg for discovering this one!)
Andreas Sp<EFBFBD>ngberg for discovering this one!)
* Added ExtraSpace(). (Scaling support hack...)
* Disabled colorkeying. (Ruins some RGBA fonts!)
*/
@ -42,7 +42,7 @@
#ifndef __SOFONT_H
#define __SOFONT_H
#include "glSDL.h"
#include "sdl_compat.h"
class SoFont
{

View File

@ -29,8 +29,8 @@ TODO: tables as needed when loading banks.
#include <stdlib.h>
#include <string.h>
#include "logger.h"
#include "glSDL.h"
#include "SDL_image.h"
#include "sdl_compat.h"
#include <SDL3_image/SDL_image.h>
#include "sprite.h"
#include "filters.h"
@ -177,7 +177,7 @@ s_sprite_t *s_new_sprite_b(s_bank_t *b, unsigned frame)
else
{
if(s->surface)
SDL_FreeSurface(s->surface);
SDL_DestroySurface(s->surface);
s->surface = NULL;
}
return s;
@ -212,7 +212,7 @@ void s_delete_sprite_b(s_bank_t *b, unsigned frame)
if(!b->sprites[frame])
return;
if(b->sprites[frame]->surface)
SDL_FreeSurface(b->sprites[frame]->surface);
SDL_DestroySurface(b->sprites[frame]->surface);
b->sprites[frame]->surface = NULL;
free(b->sprites[frame]);
b->sprites[frame] = NULL;
@ -355,44 +355,51 @@ static int extract_sprite(s_bank_t *bank, unsigned frame,
if(!s_new_sprite_b(bank, frame))
return -2;
tmp = SDL_CreateRGBSurface(src->flags,
from->w, from->h,
src->format->BitsPerPixel,
src->format->Rmask,
src->format->Gmask,
src->format->Bmask,
src->format->Amask );
const SDL_PixelFormatDetails *src_fmt =
SDL_GetPixelFormatDetails(src->format);
if(!src_fmt)
return -3;
tmp = SDL_CreateSurface(from->w, from->h, src->format);
if(!tmp)
return -3;
/* Copy the pixel data */
if(SDL_LockSurface(src) < 0)
if(!SDL_LockSurface(src))
{
log_printf(ELOG, "sprite: extract_sprite() failed to lock surface!\n");
SDL_FreeSurface(tmp);
SDL_DestroySurface(tmp);
return -4;
}
for(y = 0; y < tmp->h; ++y)
{
char *s = (char *)src->pixels + src->pitch * (y + from->y) +
from->x * src->format->BytesPerPixel;
from->x * src_fmt->bytes_per_pixel;
char *d = (char *)tmp->pixels + tmp->pitch * y;
memcpy(d, s, src->format->BytesPerPixel * tmp->w);
memcpy(d, s, src_fmt->bytes_per_pixel * tmp->w);
}
SDL_UnlockSurface(src);
/* Copy palette, if any */
if(src->format->palette)
SDL_SetColors(tmp, src->format->palette->colors, 0,
src->format->palette->ncolors);
SDL_Palette *src_palette = SDL_GetSurfacePalette(src);
if(src_palette)
SDL_SetSurfacePalette(tmp, src_palette);
/* Copy alpha and colorkey */
if(src->flags & SDL_SRCALPHA)
SDL_SetAlpha(tmp, src->flags & (SDL_SRCALPHA | SDL_RLEACCEL),
src->format->alpha);
if(src->flags & SDL_SRCCOLORKEY)
SDL_SetColorKey(tmp, src->flags & (SDL_SRCCOLORKEY | SDL_RLEACCEL),
src->format->colorkey);
/* Copy alpha modulation and blend mode */
Uint8 src_alpha = SDL_ALPHA_OPAQUE;
SDL_GetSurfaceAlphaMod(src, &src_alpha);
SDL_SetSurfaceAlphaMod(tmp, src_alpha);
SDL_BlendMode src_blend = SDL_BLENDMODE_NONE;
SDL_GetSurfaceBlendMode(src, &src_blend);
SDL_SetSurfaceBlendMode(tmp, src_blend);
/* Copy colorkey */
if(SDL_SurfaceHasColorKey(src))
{
Uint32 src_key = 0;
SDL_GetSurfaceColorKey(src, &src_key);
SDL_SetSurfaceColorKey(tmp, true, src_key);
}
bank->sprites[frame]->surface = tmp;
@ -466,21 +473,23 @@ int s_load_image(s_container_t *c, unsigned bank, const char *name)
return -1;
}
b = s_new_bank(c, bank, 1, src->clip_rect.w, src->clip_rect.h);
SDL_Rect src_clip;
SDL_GetSurfaceClipRect(src, &src_clip);
b = s_new_bank(c, bank, 1, src_clip.w, src_clip.h);
if(!b)
{
log_printf(ELOG, "sprite: Failed to allocate bank for \"%s\"!\n", name);
return -2;
}
if(extract_sprite(b, 0, src, &src->clip_rect) < 0)
if(extract_sprite(b, 0, src, &src_clip) < 0)
{
log_printf(ELOG, "sprite: Something went wrong while"
" extracting sprite \"%s\".\n", name);
return -3;
}
SDL_FreeSurface(src);
SDL_DestroySurface(src);
__run_plugins(b, 0, 1);
return 0;
}
@ -506,7 +515,7 @@ int s_load_sprite(s_container_t *c, unsigned bank, unsigned frame,
return -1;
}
from = src->clip_rect;
SDL_GetSurfaceClipRect(src, &from);
if( (from.w != b->w) || (from.h != b->h) )
{
log_printf(ELOG, "sprite: Warning: Sprite \"%s\" cropped"
@ -522,7 +531,7 @@ int s_load_sprite(s_container_t *c, unsigned bank, unsigned frame,
return -3;
}
SDL_FreeSurface(src);
SDL_DestroySurface(src);
__run_plugins(b, frame, 1);
return 0;
}
@ -582,7 +591,7 @@ int s_load_bank(s_container_t *c, unsigned bank, unsigned w, unsigned h,
}
++frame;
}
SDL_FreeSurface(src);
SDL_DestroySurface(src);
__run_plugins(b, 0, frames);
return 0;
}

View File

@ -26,7 +26,7 @@
extern "C" {
#endif
#include "glSDL.h"
#include "sdl_compat.h"
typedef enum
{

View File

@ -50,7 +50,7 @@ window_t::window_t()
window_t::~window_t()
{
if(_offscreen && surface)
SDL_FreeSurface(surface);
SDL_DestroySurface(surface);
if(selected == this)
selected = NULL;
unlink();
@ -64,7 +64,7 @@ void window_t::init(gfxengine_t *e)
ys = engine->ys;
if(_offscreen && surface)
{
SDL_FreeSurface(surface);
SDL_DestroySurface(surface);
surface = NULL;
}
_offscreen = 0;
@ -137,14 +137,10 @@ int window_t::offscreen()
return 0; // Already offscreen!
visible(0);
_offscreen = 1;
SDL_Surface *s = SDL_CreateRGBSurface(SDL_SWSURFACE,
phys_rect.w, phys_rect.h,
32, 0xff000000, 0x00ff0000,
0x0000ff00, 0x000000ff);
if(!s)
return -1;
surface = SDL_DisplayFormat(s);
SDL_FreeSurface(s);
// SDL 3: back buffer is always ARGB8888; offscreen windows use
// the same format to allow direct blits without conversion.
surface = SDL_CreateSurface(phys_rect.w, phys_rect.h,
SDL_PIXELFORMAT_ARGB8888);
if(!surface)
return -1;
return 0;
@ -163,7 +159,7 @@ void window_t::_select()
SDL_Rect r = phys_rect;
selected = this;
if(surface)
SDL_SetClipRect(surface, &r);
SDL_SetSurfaceClipRect(surface, &r);
}
@ -186,13 +182,9 @@ void window_t::invalidate(SDL_Rect *r)
rr.w = phys_rect.w;
rr.h = phys_rect.h;
phys_refresh(&rr);
glSDL_Invalidate(surface, &rr);
}
else
{
phys_refresh(r);
glSDL_Invalidate(surface, r);
}
return;
}
@ -252,7 +244,7 @@ Uint32 window_t::map_rgb(Uint8 r, Uint8 g, Uint8 b)
return 0;
if(surface)
return SDL_MapRGB(surface->format, r, g, b);
return SDL_MapSurfaceRGB(surface, r, g, b);
else
return 0xffffff;
}
@ -267,7 +259,7 @@ Uint32 window_t::map_rgb(Uint32 rgb)
Uint8 b = rgb & 0xff;
if(surface)
return SDL_MapRGB(surface->format, r, g, b);
return SDL_MapSurfaceRGB(surface, r, g, b);
else
return 0xffffff;
}
@ -287,7 +279,7 @@ void window_t::colorkey(Uint32 color)
return;
if(!_offscreen)
return;
SDL_SetColorKey(surface, SDL_SRCCOLORKEY, color);
SDL_SetSurfaceColorKey(surface, true, color);
}
void window_t::colorkey()
@ -298,7 +290,7 @@ void window_t::colorkey()
return;
if(!_offscreen)
return;
SDL_SetColorKey(surface, 0, 0);
SDL_SetSurfaceColorKey(surface, false, 0);
}
void window_t::alpha(float a)
@ -309,7 +301,8 @@ void window_t::alpha(float a)
return;
if(!_offscreen)
return;
SDL_SetAlpha(surface, SDL_SRCALPHA, (int)(a * 255.0));
SDL_SetSurfaceAlphaMod(surface, (Uint8)(a * 255.0f));
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
}
@ -480,13 +473,13 @@ void window_t::clear(SDL_Rect *r)
dr.y += phys_rect.y;
}
if((-1 == bg_bank) && (-1 == bg_frame))
SDL_FillRect(surface, &dr, bgcolor);
SDL_FillSurfaceRect(surface, &dr, bgcolor);
else
{
s_sprite_t *s = engine->get_sprite(bg_bank, bg_frame);
if(!s || !s->surface)
{
SDL_FillRect(surface, &dr, bgcolor);
SDL_FillSurfaceRect(surface, &dr, bgcolor);
return;
}
SDL_BlitSurface(s->surface, &sr, surface, &dr);
@ -511,7 +504,7 @@ void window_t::point(int _x, int _y)
r.w = x2 - _x;
r.h = y2 - _y;
if(surface)
SDL_FillRect(surface, &r, fgcolor);
SDL_FillSurfaceRect(surface, &r, fgcolor);
}
@ -531,7 +524,7 @@ void window_t::fillrect(int _x, int _y, int w, int h)
r.w = x2 - _x;
r.h = y2 - _y;
if(surface)
SDL_FillRect(surface, &r, fgcolor);
SDL_FillSurfaceRect(surface, &r, fgcolor);
}
@ -560,7 +553,7 @@ void window_t::fillrect_fxp(int _x, int _y, int w, int h)
r.w = w;
r.h = h;
if(surface)
SDL_FillRect(surface, &r, fgcolor);
SDL_FillSurfaceRect(surface, &r, fgcolor);
}

View File

@ -213,7 +213,7 @@
#ifndef _WINDOW_H_
#define _WINDOW_H_
#include "glSDL.h"
#include "sdl_compat.h"
class gfxengine_t;

148
kobo.cpp
View File

@ -440,14 +440,14 @@ int KOBO_main::quit_requested()
{
switch(e.type)
{
case SDL_QUIT:
case SDL_EVENT_QUIT:
exit_game_fast = 1;
break;
case SDL_VIDEOEXPOSE:
case SDL_EVENT_WINDOW_EXPOSED:
gengine->invalidate();
break;
case SDL_KEYUP:
switch(e.key.keysym.sym)
case SDL_EVENT_KEY_UP:
switch(e.key.key)
{
case SDLK_ESCAPE:
if(escape_hammering())
@ -459,9 +459,7 @@ int KOBO_main::quit_requested()
break;
}
}
if(exit_game_fast)
return 1;
return SDL_QuitRequested();
return exit_game_fast ? 1 : 0;
}
@ -637,7 +635,6 @@ int KOBO_main::init_display(prefs_t *p)
int dw, dh; // Display size
int gw, gh; // Game "window" size
gengine->title("Kobo Deluxe " VERSION, "kobodl");
gengine->driver((gfx_drivers_t)p->videodriver);
dw = p->width;
dh = p->height;
@ -691,10 +688,7 @@ int KOBO_main::init_display(prefs_t *p)
gengine->size(dw, dh);
gengine->mode(0, p->fullscreen);
gengine->doublebuffer(p->doublebuf);
gengine->pages(p->pages);
gengine->vsync(p->vsync);
gengine->shadow(p->shadow);
gengine->cursor(0);
gengine->period(game.speed);
@ -1088,7 +1082,7 @@ int KOBO_main::load_graphics(prefs_t *p)
if(gd->flags & KOBO_NODITHER || !p->use_dither)
gengine->dither(-1);
else
gengine->dither(p->dither_type, p->broken_rgba8);
gengine->dither(p->dither_type, 0);
// Alpha channels
if(gd->flags & KOBO_NOALPHA || !p->alpha)
@ -1205,7 +1199,7 @@ int KOBO_main::load_graphics(prefs_t *p)
return -10;
RGN_FreeRegion(logo_region);
logo_region = RGN_ScanMask(s->surface,
SDL_MapRGB(s->surface->format, 255, 255, 255));
SDL_MapSurfaceRGB(s->surface, 255, 255, 255));
if(!logo_region)
{
log_printf(ELOG, "Could not create logo region!\n");
@ -1239,34 +1233,40 @@ int KOBO_main::load_sounds(prefs_t *p, int render_all)
int KOBO_main::init_js(prefs_t *p)
{
/* Activate Joystick sub-sys if we are using it */
if(p->use_joystick)
// SDL 3 joystick API: SDL_GetJoysticks returns an array of instance
// IDs that must be SDL_free()d. SDL_OpenJoystick takes one of those
// IDs and returns the SDL_Joystick handle.
if(!p->use_joystick)
return 0;
if(!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
{
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
{
log_printf(ELOG, "Error setting up joystick!\n");
log_printf(ELOG, "Error setting up joystick: %s\n",
SDL_GetError());
return -1;
}
p->number_of_joysticks = SDL_NumJoysticks();
if(p->number_of_joysticks > 0)
{
SDL_JoystickEventState(SDL_ENABLE);
if(p->joystick_no >= p->number_of_joysticks)
p->joystick_no = 0;
joystick = SDL_JoystickOpen(p->joystick_no);
if(!joystick)
{
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return -2;
}
}
else
int count = 0;
SDL_JoystickID *ids = SDL_GetJoysticks(&count);
p->number_of_joysticks = count;
if(count <= 0)
{
SDL_free(ids);
log_printf(ELOG, "No joysticks found!\n");
joystick = NULL;
return -3;
}
if(p->joystick_no < 0 || p->joystick_no >= count)
p->joystick_no = 0;
joystick = SDL_OpenJoystick(ids[p->joystick_no]);
SDL_free(ids);
if(!joystick)
{
log_printf(ELOG, "Failed to open joystick: %s\n",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return -2;
}
return 0;
}
@ -1276,14 +1276,11 @@ void KOBO_main::close_js()
{
if(!SDL_WasInit(SDL_INIT_JOYSTICK))
return;
if(!joystick)
return;
if(SDL_JoystickOpened(0))
SDL_JoystickClose(joystick);
if(joystick)
{
SDL_CloseJoystick(joystick);
joystick = NULL;
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
@ -1597,8 +1594,8 @@ void kobo_gfxengine_t::frame()
int k, ms;
switch (ev.type)
{
case SDL_KEYDOWN:
switch(ev.key.keysym.sym)
case SDL_EVENT_KEY_DOWN:
switch(ev.key.key)
{
#ifdef PROFILE_AUDIO
case SDLK_F10:
@ -1607,7 +1604,7 @@ void kobo_gfxengine_t::frame()
case SDLK_F12:
audio_print_info();
break;
case SDLK_r:
case SDLK_R:
{
audio_channel_stop(-1, -1);
int startt = SDL_GetTicks();
@ -1626,9 +1623,9 @@ void kobo_gfxengine_t::frame()
break;
case SDLK_RETURN:
ms = SDL_GetModState();
if(ms & (KMOD_CTRL | KMOD_SHIFT | KMOD_META))
if(ms & (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_GUI))
break;
if(!(ms & KMOD_ALT))
if(!(ms & SDL_KMOD_ALT))
break;
km.pause_game();
prefs->fullscreen = !prefs->fullscreen;
@ -1637,32 +1634,29 @@ void kobo_gfxengine_t::frame()
OS_RESTART_VIDEO;
stop();
return;
case SDLK_PRINT:
case SDLK_SYSREQ:
case SDLK_PRINTSCREEN:
// FIXME: Doesn't this trigger when entering names and stuff...?
case SDLK_s:
case SDLK_S:
gengine->screenshot();
break;
default:
break;
}
k = gamecontrol.map(ev.key.keysym.sym);
k = gamecontrol.map(ev.key.key);
gamecontrol.press(k);
gsm.press(k, ev.key.keysym.unicode);
// SDL 3 dropped per-keydown unicode; text input is via
// SDL_EVENT_TEXT_INPUT. Pass 0 for the unicode arg until
// the high-score name-entry path is re-wired.
gsm.press(k, 0);
break;
case SDL_KEYUP:
if((ev.key.keysym.sym == SDLK_ESCAPE) && km.escape_hammering())
case SDL_EVENT_KEY_UP:
if((ev.key.key == SDLK_ESCAPE) && km.escape_hammering())
{
km.pause_game();
prefs->fullscreen = 0;
prefs->videodriver = (int)GFX_DRIVER_SDL2D;
prefs->width = 640;
prefs->height = 480;
prefs->aspect = 1000;
prefs->depth = 0;
prefs->doublebuf = 0;
prefs->pages = -1;
prefs->shadow = 1;
prefs->scalemode = (int)GFX_SCALE_NEAREST;
prefs->brightness = 100;
prefs->contrast = 100;
@ -1674,7 +1668,7 @@ void kobo_gfxengine_t::frame()
gsm.push(&st_error);
return;
}
k = gamecontrol.map(ev.key.keysym.sym);
k = gamecontrol.map(ev.key.key);
if(k == SDLK_PAUSE)
{
gamecontrol.press(BTN_PAUSE);
@ -1686,19 +1680,25 @@ void kobo_gfxengine_t::frame()
gsm.release(k);
}
break;
case SDL_VIDEOEXPOSE:
case SDL_EVENT_TEXT_INPUT:
// SDL 3 delivers typed characters via separate text-input
// events when SDL_StartTextInput is active. Forward each
// byte of the UTF-8 string as a unicode value to the
// current gamestate (used by the high-score name-entry).
for(const char *c = ev.text.text; *c; ++c)
gsm.press(-1, (unsigned char)*c);
break;
case SDL_EVENT_WINDOW_EXPOSED:
gengine->invalidate();
break;
case SDL_ACTIVEEVENT:
// Any type of focus loss should activate pause mode!
if(!ev.active.gain)
case SDL_EVENT_WINDOW_FOCUS_LOST:
km.pause_game();
break;
case SDL_QUIT:
case SDL_EVENT_QUIT:
/*gsm.press(BTN_CLOSE);*/
km.brutal_quit();
break;
case SDL_JOYBUTTONDOWN:
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
if(ev.jbutton.button == km.js_fire)
{
gamecontrol.press(BTN_FIRE);
@ -1710,14 +1710,14 @@ void kobo_gfxengine_t::frame()
gsm.press(BTN_START);
}
break;
case SDL_JOYBUTTONUP:
case SDL_EVENT_JOYSTICK_BUTTON_UP:
if(ev.jbutton.button == km.js_fire)
{
gamecontrol.release(BTN_FIRE);
gsm.release(BTN_FIRE);
}
break;
case SDL_JOYAXISMOTION:
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
// FIXME: We will want to allow these to be
// redefined, but for now, this works ;-)
if(ev.jaxis.axis == km.js_lr)
@ -1762,7 +1762,8 @@ void kobo_gfxengine_t::frame()
}
}
case SDL_MOUSEMOTION:
break;
case SDL_EVENT_MOUSE_MOTION:
mouse_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs;
if(prefs->use_mouse)
@ -1770,9 +1771,9 @@ void kobo_gfxengine_t::frame()
mouse_x - 8 - MARGIN - WSIZE/2,
mouse_y - MARGIN - WSIZE/2);
break;
case SDL_MOUSEBUTTONDOWN:
mouse_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
mouse_x = (int)(ev.button.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.button.y / gengine->yscale()) - km.yoffs;
gsm.press(BTN_FIRE);
if(prefs->use_mouse)
{
@ -1794,9 +1795,9 @@ void kobo_gfxengine_t::frame()
gamecontrol.press(BTN_FIRE);
}
break;
case SDL_MOUSEBUTTONUP:
mouse_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs;
case SDL_EVENT_MOUSE_BUTTON_UP:
mouse_x = (int)(ev.button.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.button.y / gengine->yscale()) - km.yoffs;
if(prefs->use_mouse)
{
gamecontrol.mouse_position(
@ -2086,8 +2087,9 @@ int main(int argc, char *argv[])
if(prefs->cmd_noparachute)
{
// SDL 3 has no parachute / signal handler by default.
SDL_Quit();
SDL_Init(SDL_INIT_NOPARACHUTE);
SDL_Init(0);
}
km.open_logging(prefs);

View File

@ -25,7 +25,7 @@
#include "config.h"
#include "logger.h"
#include "glSDL.h"
#include "sdl_compat.h"
#define LOG_BUFFER 1024
@ -100,6 +100,7 @@ static inline int log_write_raw(int target, const char *text)
/* We should never get here. */
assert(0);
}
return 0;
}
@ -187,8 +188,9 @@ static inline void put_timestamp(int level)
{
if(l_targets[l_levels[level].target].flags & LOG_TIMESTAMP)
{
char buf[16];
snprintf(buf, sizeof(buf)-1, "[%d] ", SDL_GetTicks() - start_time);
char buf[24];
snprintf(buf, sizeof(buf)-1, "[%llu] ",
(unsigned long long)(SDL_GetTicks() - start_time));
log_write_raw(l_levels[level].target, buf);
}
}

View File

@ -24,7 +24,7 @@
#ifndef _KOBO_MANAGE_H_
#define _KOBO_MANAGE_H_
#include "glSDL.h"
#include "sdl_compat.h"
#include "score.h"
class _manage
@ -87,8 +87,8 @@ class _manage
static void lost_myship();
static void destroyed_a_core();
static void add_score(int sc);
static void key_down(SDLKey sym);
static void key_up(SDLKey sym);
static void key_down(SDL_Keycode sym);
static void key_up(SDL_Keycode sym);
static int title_blank() { return blank; }
static void select_next(int redraw_map = 1);
static void select_prev(int redraw_map = 1);

View File

@ -177,41 +177,7 @@ void video_options_t::build()
}
space();
yesno("Fullscreen Display", &prf->fullscreen, OS_RESTART_VIDEO);
list("Display Depth", &prf->depth, OS_RESTART_VIDEO);
item("Default", 0);
item("8 bits", 8);
item("15 bits", 15);
item("16 bits", 16);
item("24 bits", 24);
item("32 bits", 32);
#if defined(HAVE_OPENGL) && !defined(GLSDL_OFF)
list("Display Driver", &prf->videodriver, OS_RESTART_VIDEO |
OS_REBUILD);
item("SDL 2D", GFX_DRIVER_SDL2D);
item("OpenGL/glSDL", GFX_DRIVER_GLSDL);
#else
prf->videodriver = GFX_DRIVER_SDL2D;
#endif
space();
xoffs = 0.55;
switch(prf->videodriver)
{
case GFX_DRIVER_SDL2D:
list("Display Buffering Mode", &prf->doublebuf, OS_RESTART_VIDEO);
item("Single", 0);
item("Double", 1);
yesno("Software Shadow Buffer", &prf->shadow, OS_RESTART_VIDEO);
break;
case GFX_DRIVER_GLSDL:
yesno("Vertical Retrace Sync", &prf->vsync, OS_RESTART_VIDEO);
break;
}
list("Display Buffer Pages", &prf->pages, OS_UPDATE_ENGINE);
item("Assume Standard", -1);
item("Always Repaint", 0);
item("Single", 1);
item("Double", 2);
item("Triple", 3);
big();
space();
xoffs = 0.5;
@ -250,13 +216,9 @@ void graphics_options_t::build()
item("2x2 Filter", 0);
item("4x4 Filter", 1);
item("Random", 2);
switch(prf->videodriver)
{
case GFX_DRIVER_GLSDL:
yesno("Broken RGBA8 (OpenGL)", &prf->broken_rgba8,
OS_RELOAD_GRAPHICS);
break;
}
// "Broken RGBA8" was a glSDL workaround for cards that supported
// RGB8 but not RGBA8 textures; SDL 3's renderer handles this
// transparently.
space();
yesno("Alpha Blending", &prf->alpha, OS_RELOAD_GRAPHICS);
space();
@ -403,11 +365,15 @@ void control_options_t::build()
item("5 frames", 5);
space();
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
if(!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
label("Could not initialize joysticks!");
else
{
prf->number_of_joysticks = SDL_NumJoysticks();
// SDL 3: SDL_NumJoysticks → SDL_GetJoysticks(&count).
int njs = 0;
SDL_JoystickID *jids = SDL_GetJoysticks(&njs);
SDL_free(jids);
prf->number_of_joysticks = njs;
yesno("Use Joystick", &prf->use_joystick,
OS_RESTART_INPUT | OS_REBUILD);
if(prf->use_joystick)

View File

@ -82,25 +82,18 @@ void prefs_t::init()
comment("--- Video settings -------------------------");
yesno("fullscreen", fullscreen, 0); desc("Fullscreen Display");
key("videodriver", videodriver, GFX_DRIVER_SDL2D);
desc("Display Driver");
key("width", width, 640); desc("Horizontal Resolution");
key("height", height, 480); desc("Vertical Resolution");
key("aspect", aspect, 1000); desc("Pixel Aspect Ratio");
key("depth", depth, 0); desc("Display Depth");
key("maxfps", max_fps, 100); desc("Maximum fps");
key("maxfpsstrict", max_fps_strict, 0); desc("Strictly Regulated fps");
key("buffer", doublebuf, 1); desc("Display Buffer Mode");
yesno("shadow", shadow, 1); desc("Use Software Shadow Buffer");
key("videomode", videomode, 0x04330); desc("Video Mode");
yesno("vsync", vsync, 1); desc("Enable Vertical Sync");
key("videopages", pages, -1); desc("Number of Video Pages");
comment("--- Graphics settings ----------------------");
key("scalemode", scalemode, 1); desc("Scaling Filter Mode");
yesno("dither", use_dither, 1); desc("Use Dithering");
key("dither_type", dither_type, 0); desc("Dither Type");
yesno("broken_rgba8", broken_rgba8, 0); desc("Broken RGBA (OpenGL)");
yesno("alpha", alpha, 1); desc("Use Alpha Blending");
key("brightness", brightness, 100); desc("Brightness");
key("contrast", contrast, 100); desc("Contrast");
@ -120,6 +113,14 @@ void prefs_t::init()
key("release", o_release, 50, 0); desc("Limiter Speed");
key("internalres", o_internalres, 1, 0); desc("Texture Resolution");
// SDL 1.2-era video keys, ignored under SDL 3.
key("videodriver", o_videodriver, 0, 0); desc("Display Driver");
key("depth", o_depth, 0, 0); desc("Display Depth");
key("buffer", o_doublebuf, 1, 0); desc("Display Buffer Mode");
yesno("shadow", o_shadow, 1, 0); desc("Software Shadow Buffer");
key("videopages", o_pages, -1, 0); desc("Number of Video Pages");
yesno("broken_rgba8", o_broken_rgba8, 0, 0); desc("Broken RGBA (OpenGL)");
comment("--- Temporary variables --------------------");
key("last_profile", last_profile, 0);
desc("Last used player profile");
@ -170,8 +171,4 @@ void prefs_t::postload()
log_printf(ELOG, "Radar sweep mode broken and disabled!\n");
scrollradar = 2;
}
// Some unimplemented enums were removed in 0.4.1
if(videodriver != GFX_DRIVER_SDL2D)
videodriver = GFX_DRIVER_GLSDL;
}

15
prefs.h
View File

@ -73,24 +73,18 @@ class prefs_t : public config_parser_t
//Video settings
int fullscreen; //Use fullscreen mode
int videodriver; //Internal video driver
int width; //Screen/window width
int height; //Screen/window height
int aspect; //Pixel aspect ratio * 1000
int depth; //Bits per pixel
int max_fps; //Maximum fps
int max_fps_strict; //Strictly regulated fps limiter
int doublebuf; //Use double buffering
int shadow; //Use software shadow buffer
int videomode; //New video mode codes
int videomode; //Selected entry from the vidmodes table
int vsync; //Vertical (retrace) sync
int pages; //Number of physical video pages
//Graphics settings
int scalemode; //Scaling filter mode
int use_dither;
int dither_type;
int broken_rgba8; //For some OpenGL setups
int alpha; //Alpha blending
int brightness; //Graphics brightness
int contrast; //Graphics contrast
@ -108,6 +102,13 @@ class prefs_t : public config_parser_t
int o_threshold; //Limiter threshold
int o_release; //Limiter release rate
int o_internalres; //Internal resolution for OpenGL
// SDL 1.2 video settings, read for back-compat but unused under SDL 3
int o_videodriver;
int o_depth;
int o_doublebuf;
int o_shadow;
int o_pages;
int o_broken_rgba8;
//"Hidden" stuff ("to remember until next startup")
int last_profile; //Last used player profile

View File

@ -24,7 +24,7 @@
#define _KOBO_RANDOM_H_
#include <time.h>
#include "glSDL.h"
#include "sdl_compat.h"
class rand_num_t
{

View File

@ -22,7 +22,7 @@
#ifndef _A_TOOLS_H_
#define _A_TOOLS_H_
#include "glSDL.h"
#include "sdl_compat.h"
#include <string.h>
#include "kobolog.h"
#include "a_types.h"

View File

@ -22,7 +22,7 @@
#ifndef _A_TYPES_H_
#define _A_TYPES_H_
#include "glSDL.h"
#include "sdl_compat.h"
/* Builtin patch drivers */
typedef enum

View File

@ -832,15 +832,15 @@ int audio_wave_load(int wid, const char *name, int looped)
{
switch (spec.format)
{
case AUDIO_S8:
case SDL_AUDIO_S8:
wavetab[wid].format = AF_MONO8;
break;
case AUDIO_S16SYS:
case SDL_AUDIO_S16:
wavetab[wid].format = AF_MONO16;
break;
default:
log_printf(ELOG, "sound_load(): Unsupported wave format!\n");
SDL_FreeWAV((Uint8 *)(wavetab[wid].data.si8));
SDL_free(wavetab[wid].data.si8);
res = -1;
break;
}
@ -862,7 +862,7 @@ int audio_wave_load(int wid, const char *name, int looped)
audio_wave_load_mem(wid, data, size, looped);
if(using_loadwav)
SDL_FreeWAV(data);
SDL_free(data);
else
free(data);

View File

@ -45,10 +45,25 @@
#include "audiodev.h"
#endif
#include "SDL.h"
#include "SDL_audio.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_audio.h>
#include "logger.h"
/*
* SDL 3 audio bridge.
*
* SDL 1.2 pushed callbacks into a fixed output buffer; SDL 3 uses
* SDL_AudioStream as a pull-style queue with an "I want more data"
* callback. The engine's existing _audio_callback() still fills a
* Sint16 stereo buffer of an arbitrary requested byte length, so we
* keep it intact and add a small adapter (sdl3_stream_callback) that
* runs it on a scratch buffer and pushes the result into the stream.
*/
static SDL_AudioStream *_sdl_stream = NULL;
static SDL_AudioDeviceID _sdl_audio_dev = 0;
static Uint8 *_sdl_scratch = NULL;
static int _sdl_scratch_size = 0;
#include "a_struct.h"
#include "a_commands.h"
#include "a_control.h"
@ -567,50 +582,63 @@ static int _start_oss_output()
}
static void SDLCALL _sdl3_stream_callback(void *userdata,
SDL_AudioStream *stream, int additional, int total)
{
(void)userdata; (void)total;
if(additional <= 0)
return;
if(additional > _sdl_scratch_size)
{
Uint8 *p = realloc(_sdl_scratch, additional);
if(!p)
return;
_sdl_scratch = p;
_sdl_scratch_size = additional;
}
_audio_callback(NULL, _sdl_scratch, additional);
SDL_PutAudioStreamData(stream, _sdl_scratch, additional);
}
static int _start_SDL_output(void)
{
SDL_AudioSpec as;
SDL_AudioSpec audiospec;
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
if(!SDL_InitSubSystem(SDL_INIT_AUDIO))
{
log_printf(ELOG, "audio.c: SDL_InitSubSystem failed: %s\n",
SDL_GetError());
return -2;
}
as.freq = a_settings.samplerate;
as.format = AUDIO_S16SYS;
as.channels = 2;
as.samples = (Uint16)a_settings.output_buffersize;
as.callback = _audio_callback;
if(SDL_OpenAudio(&as, &audiospec) < 0)
SDL_AudioSpec spec;
SDL_zero(spec);
spec.freq = a_settings.samplerate;
spec.format = SDL_AUDIO_S16;
spec.channels = 2;
_sdl_stream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&spec, _sdl3_stream_callback, NULL);
if(!_sdl_stream)
{
log_printf(ELOG, "audio.c: SDL_OpenAudioDeviceStream failed: %s\n",
SDL_GetError());
return -3;
if(audiospec.format != AUDIO_S16SYS)
{
log_printf(ELOG, "audio.c: ERROR: Only 16 bit output supported!\n");
SDL_CloseAudio();
return -4;
}
_sdl_audio_dev = SDL_GetAudioStreamDevice(_sdl_stream);
if(audiospec.channels != 2)
// SDL 3 may give us a different frequency than requested if the
// device only supports certain rates; query the actual format.
SDL_AudioSpec dev_spec;
int dev_frames = 0;
if(SDL_GetAudioDeviceFormat(_sdl_audio_dev, &dev_spec, &dev_frames))
{
log_printf(ELOG, "audio.c: ERROR: Only stereo output supported!\n");
SDL_CloseAudio();
return -5;
if(dev_spec.freq != a_settings.samplerate)
{
log_printf(WLOG, "audio.c: Requested %d Hz, got %d Hz.\n",
a_settings.samplerate, dev_spec.freq);
a_settings.samplerate = dev_spec.freq;
}
if(a_settings.samplerate != audiospec.freq)
{
log_printf(ELOG, "audio.c: Warning: Requested fs=%d Hz, but"
"got %d Hz.\n", a_settings.samplerate,
audiospec.freq);
a_settings.samplerate = audiospec.freq;
}
if((unsigned)audiospec.samples != a_settings.output_buffersize)
{
log_printf(ELOG, "audio.c: Warning: Requested %u sample"
"buffer, but got %u samples.\n",
a_settings.output_buffersize, audiospec.samples);
a_settings.output_buffersize = audiospec.samples;
}
if(a_settings.output_buffersize > MAX_BUFFER_SIZE)
@ -622,12 +650,13 @@ static int _start_SDL_output(void)
mixbuf = calloc(1, a_settings.buffersize * sizeof(int) * 2);
if(!mixbuf)
{
SDL_CloseAudio();
SDL_DestroyAudioStream(_sdl_stream);
_sdl_stream = NULL;
return -1;
}
_audio_running = 1;
SDL_PauseAudio(0);
SDL_ResumeAudioStreamDevice(_sdl_stream);
return 0;
}
@ -710,7 +739,17 @@ static void _stop_output(void)
#endif
}
else
SDL_CloseAudio();
{
if(_sdl_stream)
{
SDL_DestroyAudioStream(_sdl_stream);
_sdl_stream = NULL;
_sdl_audio_dev = 0;
}
free(_sdl_scratch);
_sdl_scratch = NULL;
_sdl_scratch_size = 0;
}
free(mixbuf);
mixbuf = NULL;
}
@ -736,8 +775,8 @@ void audio_lock(void)
++_audio_mutex_locked;
#endif
}
else
SDL_LockAudio();
else if(_sdl_stream)
SDL_LockAudioStream(_sdl_stream);
}
@ -756,8 +795,8 @@ void audio_unlock(void)
}
#endif
}
else
SDL_UnlockAudio();
else if(_sdl_stream)
SDL_UnlockAudioStream(_sdl_stream);
}
#endif

View File

@ -21,7 +21,8 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "glSDL.h"
#include "sdl_compat.h"
#include "gfxengine.h"
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846 /* pi */
@ -335,16 +336,15 @@ void st_game_t::enter()
"Please, check your installation.");
gsm.change(&st_error);
}
if(prefs->mousecapture)
if(SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
SDL_WM_GrabInput(SDL_GRAB_ON);
if(prefs->mousecapture && gengine->window_handle())
SDL_SetWindowMouseGrab(gengine->window_handle(), true);
}
void st_game_t::leave()
{
if(SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON)
SDL_WM_GrabInput(SDL_GRAB_OFF);
if(gengine->window_handle())
SDL_SetWindowMouseGrab(gengine->window_handle(), false);
st_intro_title.inext = &st_intro_instructions;
st_intro_title.duration = INTRO_TITLE_TIME + 2000;
st_intro_title.mode = 0;
@ -353,16 +353,15 @@ void st_game_t::leave()
void st_game_t::yield()
{
if(SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON)
SDL_WM_GrabInput(SDL_GRAB_OFF);
if(gengine->window_handle())
SDL_SetWindowMouseGrab(gengine->window_handle(), false);
}
void st_game_t::reenter()
{
if(prefs->mousecapture)
if(SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
SDL_WM_GrabInput(SDL_GRAB_ON);
if(prefs->mousecapture && gengine->window_handle())
SDL_SetWindowMouseGrab(gengine->window_handle(), true);
}
@ -878,12 +877,14 @@ void new_player_t::open()
currentIndex = 0;
editing = 1;
build_all();
SDL_EnableUNICODE(1);
if(gengine->window_handle())
SDL_StartTextInput(gengine->window_handle());
}
void new_player_t::close()
{
SDL_EnableUNICODE(0);
if(gengine->window_handle())
SDL_StopTextInput(gengine->window_handle());
clean();
}
@ -1599,7 +1600,6 @@ void st_options_base_t::select(int tag)
gengine->timefilter(prefs->timefilter * 0.01f);
gengine->interpolation(prefs->filter);
gengine->vsync(prefs->vsync);
gengine->pages(prefs->pages);
}
cfg_form->clearstatus(OS_UPDATE);