WIP: port renderer + supporting layers to SDL 3 (builds clean)

Completes phases 3-6 of the migration plan. After this commit, the
codebase compiles and links against pure SDL 3 (no compat shim). The
binary is unverified at runtime but the build is clean.

Renderer (graphics/gfxengine.{cpp,h}):
- Replace SDL_SetVideoMode + SDL_Flip + page-flip machinery with
  SDL_CreateWindow + SDL_CreateRenderer + a streaming SDL_Texture.
  The CPU-side back buffer (softbuf) is now always ARGB8888; legacy
  HW/double-buffer/page flags are dead.
- flip() does a single full-frame SDL_UpdateTexture + SDL_RenderClear
  + SDL_RenderTexture + SDL_RenderPresent each frame. The dirtyrect
  iteration is retained only to drive widget phys_refresh() callbacks.
- SDL_SetRenderLogicalPresentation gives us letterbox scaling for free.
- SDL_SetRenderVSync replaces glSDL_VSync.
- title()/cursor() ported to SDL 3 window/cursor APIs.

Surface API (graphics/sprite.c, filters.c, window.cpp, sofont.cpp,
region.c):
- SDL_FreeSurface → SDL_DestroySurface
- SDL_FillRect → SDL_FillSurfaceRect
- SDL_SetClipRect → SDL_SetSurfaceClipRect
- SDL_SetColorKey(s, flags, key) → SDL_SetSurfaceColorKey(s, bool, key)
- SDL_SetAlpha(s, flags, a) → SDL_SetSurfaceAlphaMod + BlendMode
- SDL_SetColors → SDL_SetSurfacePalette
- SDL_CreateRGBSurface(...) → SDL_CreateSurface(w, h, format_enum)
- SDL_DisplayFormat[Alpha] → SDL_ConvertSurface(s, ARGB8888)
- SDL_MapRGB(surf->format, ...) → SDL_MapSurfaceRGB(surf, ...)
- surface->format->Field → SDL_GetPixelFormatDetails(surf->format)->field
- surface->format->Amask test → SDL_ISPIXELFORMAT_ALPHA
- surface->clip_rect → SDL_GetSurfaceClipRect

Events (kobo.cpp):
- SDL_{KEYDOWN,KEYUP,QUIT,VIDEOEXPOSE,MOUSE*,JOY*} → SDL_EVENT_*
- SDL_ACTIVEEVENT (with gain check) → SDL_EVENT_WINDOW_FOCUS_LOST
- ev.key.keysym.sym → ev.key.key (.unicode → text input, deferred)
- SDLK_PRINT/SYSREQ → SDLK_PRINTSCREEN; lowercase SDLK_p/y/n → uppercase
- ev.motion.x/y for button events → ev.button.x/y (struct layout differs)
- KMOD_META → SDL_KMOD_GUI
- SDL_INIT_NOPARACHUTE → SDL_Init(0) (no parachute by default in SDL 3)

Keymap (gamectl.{cpp,h}):
- SDLKey → SDL_Keycode
- SDLK_KP{1..9} → SDLK_KP_{1..9}

Stubs deferred for later phases (binary will run but with degraded
behavior):
- sound/audio.c: _start_SDL_output returns -1 immediately; SDL 1.2
  audio callback model needs full rewrite onto SDL_AudioStream.
- kobo.cpp init_js/close_js: SDL 3 joystick API uses instance IDs;
  re-port deferred.
- states.cpp SDL_WM_GrabInput/SDL_EnableUNICODE: need window pointer
  exposed from gfxengine; stubbed.
- gamectl.cpp SDL_EnableKeyRepeat: removed; relies on ev.key.repeat.

Other adjustments:
- options.cpp: GFX_DRIVER_GLSDL menu paths removed; show vsync option
  always (renderer handles it).
- sound/a_wave.c: AUDIO_S8/AUDIO_S16SYS → SDL_AUDIO_S8/S16, SDL_FreeWAV
  → SDL_free.
- gfxengine.cpp: SDL_putenv("SDL_VIDEO_CENTERED=1") obsolete (handled
  by SDL_CreateWindow defaults).

🤖 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 21:54:55 +03:00
parent 762b095462
commit f11b6516da
No known key found for this signature in database
GPG Key ID: 89AE9EAA3B6FDE7C
15 changed files with 378 additions and 386 deletions

View File

@ -54,7 +54,10 @@ void gamecontrol_t::init(int always_fire)
down = 0; down = 0;
shot = 0; shot = 0;
movekey_pressed = 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_delay = delay;
r_interval = interval; r_interval = interval;
//Temporary kludge - should apply repeat to // SDL_EnableKeyRepeat is gone in SDL 3. Stored for future
//all switch inputs, not just the keyboard! // software-side repeat handling.
SDL_EnableKeyRepeat(delay, interval);
} }
@ -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. 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: case SDLK_ESCAPE:
return BTN_EXIT; return BTN_EXIT;
case SDLK_PAUSE: case SDLK_PAUSE:
case SDLK_p: case SDLK_P:
return BTN_PAUSE; return BTN_PAUSE;
case SDLK_SPACE: case SDLK_SPACE:
return BTN_START; return BTN_START;
@ -148,9 +150,9 @@ FIXME: This should be replaced by a configurable mapping system.
return BTN_INC; return BTN_INC;
case SDLK_KP_MINUS: case SDLK_KP_MINUS:
return BTN_DEC; return BTN_DEC;
case SDLK_y: case SDLK_Y:
return BTN_YES; return BTN_YES;
case SDLK_n: case SDLK_N:
return BTN_NO; return BTN_NO;
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
return BTN_BACK; return BTN_BACK;

View File

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

View File

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

View File

@ -40,6 +40,9 @@ gfxengine_t::gfxengine_t()
{ {
gfxengine = this; /* Uurgh! Kludge. */ gfxengine = this; /* Uurgh! Kludge. */
sdl_window = NULL;
sdl_renderer = NULL;
fb_texture = NULL;
screen_surface = NULL; screen_surface = NULL;
softbuf = NULL; softbuf = NULL;
fullwin = NULL; fullwin = NULL;
@ -443,10 +446,13 @@ void gfxengine_t::dither(int type, int _broken_rgba8)
if(_dither) if(_dither)
{ {
// 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.x = 0;
df->args.r = 1<<(screen_surface->format->Rloss-1); df->args.r = 0;
df->args.g = 1<<(screen_surface->format->Gloss-1); df->args.g = 0;
df->args.b = 1<<(screen_surface->format->Bloss-1); df->args.b = 0;
} }
else else
df->args.x = df->args.r = df->args.g = df->args.b = 0; df->args.x = df->args.r = df->args.g = df->args.b = 0;
@ -732,8 +738,8 @@ void gfxengine_t::title(const char *win, const char *icon)
{ {
_title = win; _title = win;
_icontitle = icon; _icontitle = icon;
if(screen_surface) if(sdl_window)
SDL_WM_SetCaption(_title, _icontitle); SDL_SetWindowTitle(sdl_window, _title);
} }
@ -743,97 +749,94 @@ void gfxengine_t::title(const char *win, const char *icon)
int gfxengine_t::show() int gfxengine_t::show()
{ {
int flags = 0;
if(!is_open) if(!is_open)
return -1; return -1;
if(is_showing) if(is_showing)
return 0; return 0;
if(_centered && !_fullscreen)
SDL_putenv((char *)"SDL_VIDEO_CENTERED=1");
log_printf(DLOG, "Opening screen...\n"); log_printf(DLOG, "Opening screen...\n");
if(!SDL_WasInit(SDL_INIT_VIDEO)) 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; return -2;
} }
if(_doublebuf) // SDL 3 window + renderer model.
flags |= SDL_DOUBLEBUF | SDL_HWSURFACE; //
else // The kobo engine renders into a CPU-side back buffer (softbuf) via
{ // SDL_BlitSurface/SDL_FillSurfaceRect, and uploads the result to a
if(!_shadow) // streaming SDL_Texture once per frame. This preserves the entire
flags |= SDL_HWSURFACE; // 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) if(_fullscreen)
flags |= SDL_FULLSCREEN; win_flags |= SDL_WINDOW_FULLSCREEN;
flags |= xflags; sdl_window = SDL_CreateWindow(_title ? _title : "Kobo Deluxe",
_width, _height, win_flags);
screen_surface = SDL_SetVideoMode(_width, _height, _depth, flags); if(!sdl_window)
if(!screen_surface)
{ {
log_printf(ELOG, "Failed to open display!\n"); log_printf(ELOG, "Failed to open window: %s\n", SDL_GetError());
return -3; return -3;
} }
if((screen_surface->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF) sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if(!sdl_renderer)
{ {
if(!_doublebuf) log_printf(ELOG, "Failed to create renderer: %s\n",
{ SDL_GetError());
log_printf(WLOG, "Could not get" SDL_DestroyWindow(sdl_window);
" single buffered display.\n"); sdl_window = NULL;
doublebuffer(1); return -4;
}
}
else
{
if(_doublebuf)
{
log_printf(WLOG, "Could not get"
" double buffered display.\n");
doublebuffer(0);
}
} }
if((screen_surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE) // Letterbox the back buffer when the window is resized / fullscreen.
{ SDL_SetRenderLogicalPresentation(sdl_renderer, _width, _height,
if(_shadow) SDL_LOGICAL_PRESENTATION_LETTERBOX);
{ SDL_SetRenderVSync(sdl_renderer, _vsync ? 1 : 0);
softbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
_width, _height, // The back buffer is always a CPU-side ARGB8888 surface.
screen_surface->format->BitsPerPixel, softbuf = SDL_CreateSurface(_width, _height, SDL_PIXELFORMAT_ARGB8888);
screen_surface->format->Rmask, if(!softbuf)
screen_surface->format->Gmask, {
screen_surface->format->Bmask, log_printf(ELOG, "Failed to create back buffer: %s\n",
screen_surface->format->Amask); SDL_GetError());
if(!softbuf) SDL_DestroyRenderer(sdl_renderer);
{ SDL_DestroyWindow(sdl_window);
log_printf(WLOG, "Failed to create shadow buffer! " sdl_renderer = NULL;
"Trying direct rendering.\n"); sdl_window = NULL;
shadow(0); return -5;
} }
} screen_surface = softbuf; // alias for legacy code paths
}
else fb_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888,
{ SDL_TEXTUREACCESS_STREAMING, _width, _height);
if(_shadow) if(!fb_texture)
log_printf(WLOG, "Shadow buffer requested; " {
"relying on SDL's shadow buffer.\n"); log_printf(ELOG, "Failed to create framebuffer texture: %s\n",
else SDL_GetError());
{ SDL_DestroySurface(softbuf);
log_printf(WLOG, "Could not get h/w display surface.\n"); SDL_DestroyRenderer(sdl_renderer);
shadow(0); //...which means we're using SDL's shadow. 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); cs_engine_set_size(csengine, _width, _height);
csengine->filter = use_interpolation; csengine->filter = use_interpolation;
@ -886,12 +889,27 @@ void gfxengine_t::hide(void)
w = w->next; w = w->next;
} }
if(fb_texture)
{
SDL_DestroyTexture(fb_texture);
fb_texture = NULL;
}
if(softbuf) if(softbuf)
{ {
SDL_FreeSurface(softbuf); SDL_DestroySurface(softbuf);
softbuf = NULL; softbuf = NULL;
} }
screen_surface = 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; is_showing = 0;
} }
@ -940,13 +958,15 @@ void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
/* Clip to screen (stolen from SDL_surface.c) */ /* Clip to screen (stolen from SDL_surface.c) */
SDL_Rect dr = *rect; SDL_Rect dr = *rect;
SDL_Rect screen_clip;
SDL_GetSurfaceClipRect(screen_surface, &screen_clip);
int Amin, Amax, Bmin, Bmax; int Amin, Amax, Bmin, Bmax;
/* Horizontal intersection */ /* Horizontal intersection */
Amin = dr.x; Amin = dr.x;
Amax = Amin + dr.w; Amax = Amin + dr.w;
Bmin = screen_surface->clip_rect.x; Bmin = screen_clip.x;
Bmax = Bmin + screen_surface->clip_rect.w; Bmax = Bmin + screen_clip.w;
if(Bmin > Amin) if(Bmin > Amin)
Amin = Bmin; Amin = Bmin;
dr.x = Amin; dr.x = Amin;
@ -957,8 +977,8 @@ void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
/* Vertical intersection */ /* Vertical intersection */
Amin = dr.y; Amin = dr.y;
Amax = Amin + dr.h; Amax = Amin + dr.h;
Bmin = screen_surface->clip_rect.y; Bmin = screen_clip.y;
Bmax = Bmin + screen_surface->clip_rect.h; Bmax = Bmin + screen_clip.h;
if(Bmin > Amin) if(Bmin > Amin)
Amin = Bmin; Amin = Bmin;
dr.y = Amin; dr.y = Amin;
@ -1062,7 +1082,12 @@ void gfxengine_t::cursor(int csr)
{ {
_cursor = csr; _cursor = csr;
if(screen_surface) if(screen_surface)
SDL_ShowCursor(csr); {
if(csr)
SDL_ShowCursor();
else
SDL_HideCursor();
}
} }
@ -1168,8 +1193,8 @@ void gfxengine_t::frame()
SDL_Event ev; SDL_Event ev;
while(SDL_PollEvent(&ev)) while(SDL_PollEvent(&ev))
{ {
if(ev.type == SDL_KEYDOWN) if(ev.type == SDL_EVENT_KEY_DOWN)
if(ev.key.keysym.sym == SDLK_ESCAPE) if(ev.key.key == SDLK_ESCAPE)
stop(); stop();
} }
} }
@ -1236,41 +1261,15 @@ void gfxengine_t::refresh_rect(SDL_Rect *r)
void gfxengine_t::flip() void gfxengine_t::flip()
{ {
if(!screen_surface) if(!screen_surface || !sdl_renderer || !fb_texture)
return; return;
// Init dirtyrect table flipping, if necessary. // SDL 3 has no hardware page flipping; we always present one back
switch(_pages) // buffer per frame. Keep the dirtyrect iteration only to drive the
{ // phys_refresh() callbacks that window widgets rely on; the upload
case -1: // is a single full-surface copy regardless.
if(!_doublebuf) frontpage = backpage = 0;
frontpage = backpage = 0; for(int i = 0; i < dirtyrects[backpage]; ++i)
else if(frontpage == backpage)
{
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])
{ {
@ -1282,36 +1281,12 @@ void gfxengine_t::flip()
else else
refresh_rect(&dirtytable[backpage][i]); refresh_rect(&dirtytable[backpage][i]);
} }
dirtyrects[backpage] = 0;
// Perform the actual flip or update SDL_UpdateTexture(fb_texture, NULL, softbuf->pixels, softbuf->pitch);
if(_shadow) SDL_RenderClear(sdl_renderer);
{ SDL_RenderTexture(sdl_renderer, fb_texture, NULL, NULL);
for(i = 0; i < dirtyrects[backpage]; ++i) SDL_RenderPresent(sdl_renderer);
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;
}
} }

View File

@ -208,7 +208,10 @@ class gfxengine_t
gfx_drivers_t _driver; gfx_drivers_t _driver;
gfx_scalemodes_t _scalemode; gfx_scalemodes_t _scalemode;
int _clamping; 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; SDL_Surface *softbuf;
int backpage; int backpage;
int frontpage; int frontpage;

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) 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; Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2]; 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); RGN_FreeRegion(rgn);
return NULL; 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: case 1:
gp = gp1; gp = gp1;

View File

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

View File

@ -177,7 +177,7 @@ s_sprite_t *s_new_sprite_b(s_bank_t *b, unsigned frame)
else else
{ {
if(s->surface) if(s->surface)
SDL_FreeSurface(s->surface); SDL_DestroySurface(s->surface);
s->surface = NULL; s->surface = NULL;
} }
return s; return s;
@ -212,7 +212,7 @@ void s_delete_sprite_b(s_bank_t *b, unsigned frame)
if(!b->sprites[frame]) if(!b->sprites[frame])
return; return;
if(b->sprites[frame]->surface) if(b->sprites[frame]->surface)
SDL_FreeSurface(b->sprites[frame]->surface); SDL_DestroySurface(b->sprites[frame]->surface);
b->sprites[frame]->surface = NULL; b->sprites[frame]->surface = NULL;
free(b->sprites[frame]); free(b->sprites[frame]);
b->sprites[frame] = NULL; 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)) if(!s_new_sprite_b(bank, frame))
return -2; return -2;
tmp = SDL_CreateRGBSurface(src->flags, const SDL_PixelFormatDetails *src_fmt =
from->w, from->h, SDL_GetPixelFormatDetails(src->format);
src->format->BitsPerPixel, if(!src_fmt)
src->format->Rmask, return -3;
src->format->Gmask,
src->format->Bmask, tmp = SDL_CreateSurface(from->w, from->h, src->format);
src->format->Amask );
if(!tmp) if(!tmp)
return -3; return -3;
/* Copy the pixel data */ /* Copy the pixel data */
if(SDL_LockSurface(src) < 0) if(!SDL_LockSurface(src))
{ {
log_printf(ELOG, "sprite: extract_sprite() failed to lock surface!\n"); log_printf(ELOG, "sprite: extract_sprite() failed to lock surface!\n");
SDL_FreeSurface(tmp); SDL_DestroySurface(tmp);
return -4; return -4;
} }
for(y = 0; y < tmp->h; ++y) for(y = 0; y < tmp->h; ++y)
{ {
char *s = (char *)src->pixels + src->pitch * (y + from->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; 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); SDL_UnlockSurface(src);
/* Copy palette, if any */ /* Copy palette, if any */
if(src->format->palette) SDL_Palette *src_palette = SDL_GetSurfacePalette(src);
SDL_SetColors(tmp, src->format->palette->colors, 0, if(src_palette)
src->format->palette->ncolors); SDL_SetSurfacePalette(tmp, src_palette);
/* Copy alpha and colorkey */ /* Copy alpha modulation and blend mode */
if(src->flags & SDL_SRCALPHA) Uint8 src_alpha = SDL_ALPHA_OPAQUE;
SDL_SetAlpha(tmp, src->flags & (SDL_SRCALPHA | SDL_RLEACCEL), SDL_GetSurfaceAlphaMod(src, &src_alpha);
src->format->alpha); SDL_SetSurfaceAlphaMod(tmp, src_alpha);
if(src->flags & SDL_SRCCOLORKEY) SDL_BlendMode src_blend = SDL_BLENDMODE_NONE;
SDL_SetColorKey(tmp, src->flags & (SDL_SRCCOLORKEY | SDL_RLEACCEL), SDL_GetSurfaceBlendMode(src, &src_blend);
src->format->colorkey); 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; bank->sprites[frame]->surface = tmp;
@ -466,21 +473,23 @@ int s_load_image(s_container_t *c, unsigned bank, const char *name)
return -1; 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) if(!b)
{ {
log_printf(ELOG, "sprite: Failed to allocate bank for \"%s\"!\n", name); log_printf(ELOG, "sprite: Failed to allocate bank for \"%s\"!\n", name);
return -2; 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" log_printf(ELOG, "sprite: Something went wrong while"
" extracting sprite \"%s\".\n", name); " extracting sprite \"%s\".\n", name);
return -3; return -3;
} }
SDL_FreeSurface(src); SDL_DestroySurface(src);
__run_plugins(b, 0, 1); __run_plugins(b, 0, 1);
return 0; return 0;
} }
@ -506,7 +515,7 @@ int s_load_sprite(s_container_t *c, unsigned bank, unsigned frame,
return -1; return -1;
} }
from = src->clip_rect; SDL_GetSurfaceClipRect(src, &from);
if( (from.w != b->w) || (from.h != b->h) ) if( (from.w != b->w) || (from.h != b->h) )
{ {
log_printf(ELOG, "sprite: Warning: Sprite \"%s\" cropped" 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; return -3;
} }
SDL_FreeSurface(src); SDL_DestroySurface(src);
__run_plugins(b, frame, 1); __run_plugins(b, frame, 1);
return 0; return 0;
} }
@ -582,7 +591,7 @@ int s_load_bank(s_container_t *c, unsigned bank, unsigned w, unsigned h,
} }
++frame; ++frame;
} }
SDL_FreeSurface(src); SDL_DestroySurface(src);
__run_plugins(b, 0, frames); __run_plugins(b, 0, frames);
return 0; return 0;
} }

View File

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

120
kobo.cpp
View File

@ -440,14 +440,14 @@ int KOBO_main::quit_requested()
{ {
switch(e.type) switch(e.type)
{ {
case SDL_QUIT: case SDL_EVENT_QUIT:
exit_game_fast = 1; exit_game_fast = 1;
break; break;
case SDL_VIDEOEXPOSE: case SDL_EVENT_WINDOW_EXPOSED:
gengine->invalidate(); gengine->invalidate();
break; break;
case SDL_KEYUP: case SDL_EVENT_KEY_UP:
switch(e.key.keysym.sym) switch(e.key.key)
{ {
case SDLK_ESCAPE: case SDLK_ESCAPE:
if(escape_hammering()) if(escape_hammering())
@ -459,9 +459,7 @@ int KOBO_main::quit_requested()
break; break;
} }
} }
if(exit_game_fast) return exit_game_fast ? 1 : 0;
return 1;
return SDL_QuitRequested();
} }
@ -1205,7 +1203,7 @@ int KOBO_main::load_graphics(prefs_t *p)
return -10; return -10;
RGN_FreeRegion(logo_region); RGN_FreeRegion(logo_region);
logo_region = RGN_ScanMask(s->surface, logo_region = RGN_ScanMask(s->surface,
SDL_MapRGB(s->surface->format, 255, 255, 255)); SDL_MapSurfaceRGB(s->surface, 255, 255, 255));
if(!logo_region) if(!logo_region)
{ {
log_printf(ELOG, "Could not create logo region!\n"); log_printf(ELOG, "Could not create logo region!\n");
@ -1239,52 +1237,20 @@ int KOBO_main::load_sounds(prefs_t *p, int render_all)
int KOBO_main::init_js(prefs_t *p) int KOBO_main::init_js(prefs_t *p)
{ {
/* Activate Joystick sub-sys if we are using it */ // SDL 3 joystick API uses instance IDs and SDL_GetJoysticks /
// SDL_OpenJoystick. Port deferred — disabled for now.
if(p->use_joystick) if(p->use_joystick)
{ log_printf(WLOG, "Joystick support not yet ported to SDL 3.\n");
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) p->number_of_joysticks = 0;
{ joystick = NULL;
log_printf(ELOG, "Error setting up joystick!\n");
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
{
log_printf(ELOG, "No joysticks found!\n");
joystick = NULL;
return -3;
}
}
return 0; return 0;
} }
void KOBO_main::close_js() void KOBO_main::close_js()
{ {
if(!SDL_WasInit(SDL_INIT_JOYSTICK)) // Stub: see init_js. Joystick port pending.
return;
if(!joystick)
return;
if(SDL_JoystickOpened(0))
SDL_JoystickClose(joystick);
joystick = NULL; joystick = NULL;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
} }
@ -1597,8 +1563,8 @@ void kobo_gfxengine_t::frame()
int k, ms; int k, ms;
switch (ev.type) switch (ev.type)
{ {
case SDL_KEYDOWN: case SDL_EVENT_KEY_DOWN:
switch(ev.key.keysym.sym) switch(ev.key.key)
{ {
#ifdef PROFILE_AUDIO #ifdef PROFILE_AUDIO
case SDLK_F10: case SDLK_F10:
@ -1607,7 +1573,7 @@ void kobo_gfxengine_t::frame()
case SDLK_F12: case SDLK_F12:
audio_print_info(); audio_print_info();
break; break;
case SDLK_r: case SDLK_R:
{ {
audio_channel_stop(-1, -1); audio_channel_stop(-1, -1);
int startt = SDL_GetTicks(); int startt = SDL_GetTicks();
@ -1626,9 +1592,9 @@ void kobo_gfxengine_t::frame()
break; break;
case SDLK_RETURN: case SDLK_RETURN:
ms = SDL_GetModState(); ms = SDL_GetModState();
if(ms & (KMOD_CTRL | KMOD_SHIFT | KMOD_META)) if(ms & (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_GUI))
break; break;
if(!(ms & KMOD_ALT)) if(!(ms & SDL_KMOD_ALT))
break; break;
km.pause_game(); km.pause_game();
prefs->fullscreen = !prefs->fullscreen; prefs->fullscreen = !prefs->fullscreen;
@ -1637,21 +1603,23 @@ void kobo_gfxengine_t::frame()
OS_RESTART_VIDEO; OS_RESTART_VIDEO;
stop(); stop();
return; return;
case SDLK_PRINT: case SDLK_PRINTSCREEN:
case SDLK_SYSREQ:
// FIXME: Doesn't this trigger when entering names and stuff...? // FIXME: Doesn't this trigger when entering names and stuff...?
case SDLK_s: case SDLK_S:
gengine->screenshot(); gengine->screenshot();
break; break;
default: default:
break; break;
} }
k = gamecontrol.map(ev.key.keysym.sym); k = gamecontrol.map(ev.key.key);
gamecontrol.press(k); 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; break;
case SDL_KEYUP: case SDL_EVENT_KEY_UP:
if((ev.key.keysym.sym == SDLK_ESCAPE) && km.escape_hammering()) if((ev.key.key == SDLK_ESCAPE) && km.escape_hammering())
{ {
km.pause_game(); km.pause_game();
prefs->fullscreen = 0; prefs->fullscreen = 0;
@ -1674,7 +1642,7 @@ void kobo_gfxengine_t::frame()
gsm.push(&st_error); gsm.push(&st_error);
return; return;
} }
k = gamecontrol.map(ev.key.keysym.sym); k = gamecontrol.map(ev.key.key);
if(k == SDLK_PAUSE) if(k == SDLK_PAUSE)
{ {
gamecontrol.press(BTN_PAUSE); gamecontrol.press(BTN_PAUSE);
@ -1686,19 +1654,17 @@ void kobo_gfxengine_t::frame()
gsm.release(k); gsm.release(k);
} }
break; break;
case SDL_VIDEOEXPOSE: case SDL_EVENT_WINDOW_EXPOSED:
gengine->invalidate(); gengine->invalidate();
break; break;
case SDL_ACTIVEEVENT: case SDL_EVENT_WINDOW_FOCUS_LOST:
// Any type of focus loss should activate pause mode! km.pause_game();
if(!ev.active.gain)
km.pause_game();
break; break;
case SDL_QUIT: case SDL_EVENT_QUIT:
/*gsm.press(BTN_CLOSE);*/ /*gsm.press(BTN_CLOSE);*/
km.brutal_quit(); km.brutal_quit();
break; break;
case SDL_JOYBUTTONDOWN: case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
if(ev.jbutton.button == km.js_fire) if(ev.jbutton.button == km.js_fire)
{ {
gamecontrol.press(BTN_FIRE); gamecontrol.press(BTN_FIRE);
@ -1710,14 +1676,14 @@ void kobo_gfxengine_t::frame()
gsm.press(BTN_START); gsm.press(BTN_START);
} }
break; break;
case SDL_JOYBUTTONUP: case SDL_EVENT_JOYSTICK_BUTTON_UP:
if(ev.jbutton.button == km.js_fire) if(ev.jbutton.button == km.js_fire)
{ {
gamecontrol.release(BTN_FIRE); gamecontrol.release(BTN_FIRE);
gsm.release(BTN_FIRE); gsm.release(BTN_FIRE);
} }
break; break;
case SDL_JOYAXISMOTION: case SDL_EVENT_JOYSTICK_AXIS_MOTION:
// FIXME: We will want to allow these to be // FIXME: We will want to allow these to be
// redefined, but for now, this works ;-) // redefined, but for now, this works ;-)
if(ev.jaxis.axis == km.js_lr) if(ev.jaxis.axis == km.js_lr)
@ -1762,7 +1728,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_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs; mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs;
if(prefs->use_mouse) if(prefs->use_mouse)
@ -1770,9 +1737,9 @@ void kobo_gfxengine_t::frame()
mouse_x - 8 - MARGIN - WSIZE/2, mouse_x - 8 - MARGIN - WSIZE/2,
mouse_y - MARGIN - WSIZE/2); mouse_y - MARGIN - WSIZE/2);
break; break;
case SDL_MOUSEBUTTONDOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
mouse_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs; mouse_x = (int)(ev.button.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs; mouse_y = (int)(ev.button.y / gengine->yscale()) - km.yoffs;
gsm.press(BTN_FIRE); gsm.press(BTN_FIRE);
if(prefs->use_mouse) if(prefs->use_mouse)
{ {
@ -1794,9 +1761,9 @@ void kobo_gfxengine_t::frame()
gamecontrol.press(BTN_FIRE); gamecontrol.press(BTN_FIRE);
} }
break; break;
case SDL_MOUSEBUTTONUP: case SDL_EVENT_MOUSE_BUTTON_UP:
mouse_x = (int)(ev.motion.x / gengine->xscale()) - km.xoffs; mouse_x = (int)(ev.button.x / gengine->xscale()) - km.xoffs;
mouse_y = (int)(ev.motion.y / gengine->yscale()) - km.yoffs; mouse_y = (int)(ev.button.y / gengine->yscale()) - km.yoffs;
if(prefs->use_mouse) if(prefs->use_mouse)
{ {
gamecontrol.mouse_position( gamecontrol.mouse_position(
@ -2086,8 +2053,9 @@ int main(int argc, char *argv[])
if(prefs->cmd_noparachute) if(prefs->cmd_noparachute)
{ {
// SDL 3 has no parachute / signal handler by default.
SDL_Quit(); SDL_Quit();
SDL_Init(SDL_INIT_NOPARACHUTE); SDL_Init(0);
} }
km.open_logging(prefs); km.open_logging(prefs);

View File

@ -87,8 +87,8 @@ class _manage
static void lost_myship(); static void lost_myship();
static void destroyed_a_core(); static void destroyed_a_core();
static void add_score(int sc); static void add_score(int sc);
static void key_down(SDLKey sym); static void key_down(SDL_Keycode sym);
static void key_up(SDLKey sym); static void key_up(SDL_Keycode sym);
static int title_blank() { return blank; } static int title_blank() { return blank; }
static void select_next(int redraw_map = 1); static void select_next(int redraw_map = 1);
static void select_prev(int redraw_map = 1); static void select_prev(int redraw_map = 1);

View File

@ -187,18 +187,13 @@ void video_options_t::build()
prf->videodriver = GFX_DRIVER_SDL2D; prf->videodriver = GFX_DRIVER_SDL2D;
space(); space();
xoffs = 0.55; xoffs = 0.55;
switch(prf->videodriver) // Only the SDL 2D driver remains since the SDL 3 migration; the
{ // OpenGL driver is now handled by the SDL renderer itself.
case GFX_DRIVER_SDL2D: list("Display Buffering Mode", &prf->doublebuf, OS_RESTART_VIDEO);
list("Display Buffering Mode", &prf->doublebuf, OS_RESTART_VIDEO); item("Single", 0);
item("Single", 0); item("Double", 1);
item("Double", 1); yesno("Software Shadow Buffer", &prf->shadow, OS_RESTART_VIDEO);
yesno("Software Shadow Buffer", &prf->shadow, OS_RESTART_VIDEO); yesno("Vertical Retrace Sync", &prf->vsync, 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); list("Display Buffer Pages", &prf->pages, OS_UPDATE_ENGINE);
item("Assume Standard", -1); item("Assume Standard", -1);
item("Always Repaint", 0); item("Always Repaint", 0);
@ -243,13 +238,9 @@ void graphics_options_t::build()
item("2x2 Filter", 0); item("2x2 Filter", 0);
item("4x4 Filter", 1); item("4x4 Filter", 1);
item("Random", 2); item("Random", 2);
switch(prf->videodriver) // "Broken RGBA8" was a glSDL workaround for cards that supported
{ // RGB8 but not RGBA8 textures; SDL 3's renderer handles this
case GFX_DRIVER_GLSDL: // transparently.
yesno("Broken RGBA8 (OpenGL)", &prf->broken_rgba8,
OS_RELOAD_GRAPHICS);
break;
}
space(); space();
yesno("Alpha Blending", &prf->alpha, OS_RELOAD_GRAPHICS); yesno("Alpha Blending", &prf->alpha, OS_RELOAD_GRAPHICS);
space(); space();
@ -396,11 +387,15 @@ void control_options_t::build()
item("5 frames", 5); item("5 frames", 5);
space(); space();
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) if(!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
label("Could not initialize joysticks!"); label("Could not initialize joysticks!");
else 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, yesno("Use Joystick", &prf->use_joystick,
OS_RESTART_INPUT | OS_REBUILD); OS_RESTART_INPUT | OS_REBUILD);
if(prf->use_joystick) if(prf->use_joystick)

View File

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

View File

@ -49,6 +49,29 @@
#include <SDL3/SDL_audio.h> #include <SDL3/SDL_audio.h>
#include "logger.h" #include "logger.h"
/*
* SDL 3 migration: the SDL 1.2 audio API (SDL_OpenAudio + callback model,
* AUDIO_S16SYS, SDL_LockAudio/PauseAudio/CloseAudio, SDL_AudioSpec.samples
* and .callback) is gone. SDL 3 uses SDL_OpenAudioDeviceStream with a
* pull-stream callback. Porting that bridge is a separate phase; for now
* we provide local stubs so the engine links, and _start_SDL_output()
* below returns -1 so audio is silently disabled.
*/
#define AUDIO_S16SYS 0
#define SDL_OpenAudio(a, b) (-1)
#define SDL_CloseAudio() ((void)0)
#define SDL_PauseAudio(x) ((void)0)
#define SDL_LockAudio() ((void)0)
#define SDL_UnlockAudio() ((void)0)
typedef struct {
int freq;
Uint16 format;
Uint8 channels;
Uint16 samples;
void (*callback)(void *userdata, Uint8 *stream, int len);
} kobo_legacy_audio_spec_t;
#define SDL_AudioSpec kobo_legacy_audio_spec_t
#include "a_struct.h" #include "a_struct.h"
#include "a_commands.h" #include "a_commands.h"
#include "a_control.h" #include "a_control.h"
@ -569,6 +592,10 @@ static int _start_oss_output()
static int _start_SDL_output(void) static int _start_SDL_output(void)
{ {
log_printf(WLOG, "audio.c: SDL 3 audio backend not yet ported;"
" sound disabled.\n");
return -1;
#if 0
SDL_AudioSpec as; SDL_AudioSpec as;
SDL_AudioSpec audiospec; SDL_AudioSpec audiospec;
@ -629,6 +656,7 @@ static int _start_SDL_output(void)
_audio_running = 1; _audio_running = 1;
SDL_PauseAudio(0); SDL_PauseAudio(0);
return 0; return 0;
#endif
} }

View File

@ -23,6 +23,20 @@
#include "sdl_compat.h" #include "sdl_compat.h"
#include <math.h> #include <math.h>
/*
* SDL 3 migration: SDL_WM_GrabInput and SDL_EnableUNICODE are gone.
* Replacements (SDL_SetWindowMouseGrab and SDL_StartTextInput) both
* require the window pointer, which we don't yet expose from gfxengine.
* Stub them out so the code links; mouse-capture and unicode text-input
* (high-score name entry) will be re-wired once gfxengine exposes the
* SDL_Window handle.
*/
#define SDL_GRAB_QUERY 0
#define SDL_GRAB_ON 1
#define SDL_GRAB_OFF 0
#define SDL_WM_GrabInput(x) (SDL_GRAB_OFF)
#define SDL_EnableUNICODE(x) ((void)0)
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846 /* pi */ # define M_PI 3.14159265358979323846 /* pi */
#endif #endif