diff --git a/graphics/gfxengine.h b/graphics/gfxengine.h index 5e02a9c..90545ca 100644 --- a/graphics/gfxengine.h +++ b/graphics/gfxengine.h @@ -171,6 +171,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 diff --git a/kobo.cpp b/kobo.cpp index efb3d8b..5500e8c 100644 --- a/kobo.cpp +++ b/kobo.cpp @@ -1237,20 +1237,55 @@ int KOBO_main::load_sounds(prefs_t *p, int render_all) int KOBO_main::init_js(prefs_t *p) { - // SDL 3 joystick API uses instance IDs and SDL_GetJoysticks / - // SDL_OpenJoystick. Port deferred — disabled for now. - if(p->use_joystick) - log_printf(WLOG, "Joystick support not yet ported to SDL 3.\n"); - p->number_of_joysticks = 0; - joystick = NULL; + // 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)) + { + log_printf(ELOG, "Error setting up joystick: %s\n", + SDL_GetError()); + return -1; + } + + 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; } void KOBO_main::close_js() { - // Stub: see init_js. Joystick port pending. - joystick = NULL; + if(!SDL_WasInit(SDL_INIT_JOYSTICK)) + return; + if(joystick) + { + SDL_CloseJoystick(joystick); + joystick = NULL; + } + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } @@ -1654,6 +1689,14 @@ void kobo_gfxengine_t::frame() gsm.release(k); } break; + 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; diff --git a/sound/audio.c b/sound/audio.c index cd498ab..b987f4b 100644 --- a/sound/audio.c +++ b/sound/audio.c @@ -50,27 +50,19 @@ #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. + * 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. */ -#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 +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" @@ -590,54 +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) { - log_printf(WLOG, "audio.c: SDL 3 audio backend not yet ported;" - " sound disabled.\n"); - return -1; -#if 0 - 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(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(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.output_buffersize > MAX_BUFFER_SIZE) @@ -649,14 +650,14 @@ 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; -#endif } @@ -738,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; } @@ -764,8 +775,8 @@ void audio_lock(void) ++_audio_mutex_locked; #endif } - else - SDL_LockAudio(); + else if(_sdl_stream) + SDL_LockAudioStream(_sdl_stream); } @@ -784,8 +795,8 @@ void audio_unlock(void) } #endif } - else - SDL_UnlockAudio(); + else if(_sdl_stream) + SDL_UnlockAudioStream(_sdl_stream); } #endif diff --git a/states.cpp b/states.cpp index 1a34af5..2e411a7 100644 --- a/states.cpp +++ b/states.cpp @@ -22,21 +22,8 @@ */ #include "sdl_compat.h" +#include "gfxengine.h" #include - -/* - * 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 # define M_PI 3.14159265358979323846 /* pi */ #endif @@ -349,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; @@ -367,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); } @@ -892,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(); }