From f10172d5bbbca8b78df1a7723b8e07572c723b1d Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sat, 17 Sep 2022 17:41:24 -0500 Subject: [PATCH] In theory, we have an SDL 1.2 backend. No toolchain to test it with yet. --- joeylib/joeylib.pro | 30 +- joeylib/src/3rdparty/pocketmod/pocketmod.h | 2 +- joeylib/src/jSDL12.c | 774 ++++++++++++++++++--- joeylib/src/joey.c | 2 +- joeylib/src/joey.h | 4 +- 5 files changed, 706 insertions(+), 106 deletions(-) diff --git a/joeylib/joeylib.pro b/joeylib/joeylib.pro index b860702..047f60b 100644 --- a/joeylib/joeylib.pro +++ b/joeylib/joeylib.pro @@ -1,6 +1,7 @@ JOEY = /home/scott/joey -#CONFIG += SDL12 -CONFIG += SDL2 +CONFIG += SDL12 +#CONFIG += SDL2 +#CONFIG += OURLIBS #BREW = /home/linuxbrew/.linuxbrew @@ -10,6 +11,7 @@ CONFIG -= \ app_bundle \ qt +# Determine which libraries to link against. defined(BREW,var) { # Use Homebrew libraries. STATICLIB = $$BREW/lib @@ -18,12 +20,19 @@ defined(BREW,var) { -L$$STATICLIB \ -Wl,-rpath,$$STATICLIB } else { - # Use our libraries. - #STATICLIB = $$JOEY/sdks/linux/x64/lib - #INCLUDEPATH += $$JOEY/sdks/linux/x64/include - #LIBS += \ -# -L$$STATICLIB \ -# -Wl,-rpath,$$STATICLIB + OURLIBS { + # Use our libraries. + STATICLIB = $$JOEY/sdks/linux/x64/lib + INCLUDEPATH += $$JOEY/sdks/linux/x64/include + LIBS += \ + -L$$STATICLIB \ + -Wl,-rpath,$$STATICLIB + } else { + # System libraries. + STATICLIB = /usr/lib/x86_64-linux-gnu + LIBS += \ + -Wl,-rpath,$$STATICLIB + } } INCLUDEPATH += $$PWD/src @@ -65,9 +74,8 @@ SDL2 { LIBS += \ -Wl,--enable-new-dtags \ - -lSDL2 -# $$STATICLIB/libSDL2.a \ -# -lm -ldl -lX11 -lXext -lXcursor -lXi -lXfixes -lXrandr -lXss -lpthread -lrt + $$STATICLIB/libSDL2.a \ + -lm -lasound -lm -ldl -lpthread -lpulse-simple -pthread -lpulse -pthread -lX11 -lXext -lXcursor -lXinerama -lXi -lXfixes -lXrandr -lXss -lXxf86vm -ldrm -lgbm -lwayland-egl -lwayland-client -lwayland-cursor -lxkbcommon -ldecor-0 -lpthread -lrt } OTHER_FILES += \ diff --git a/joeylib/src/3rdparty/pocketmod/pocketmod.h b/joeylib/src/3rdparty/pocketmod/pocketmod.h index d1c315e..75dafc5 100644 --- a/joeylib/src/3rdparty/pocketmod/pocketmod.h +++ b/joeylib/src/3rdparty/pocketmod/pocketmod.h @@ -87,7 +87,7 @@ struct pocketmod_context float sample; /* Current sample in tick */ }; -#ifndef POCKETMOD_IMPLEMENTATIONx +#ifdef POCKETMOD_IMPLEMENTATION /* Memorize a parameter unless the new value is zero */ #define POCKETMOD_MEM(dst, src) do { \ diff --git a/joeylib/src/jSDL12.c b/joeylib/src/jSDL12.c index add50d2..05bc997 100644 --- a/joeylib/src/jSDL12.c +++ b/joeylib/src/jSDL12.c @@ -19,155 +19,747 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#include +#include +#include #include "SDL/SDL.h" -#include "SDL/SDL_mixer.h" + +#define POCKETMOD_IMPLEMENTATION +#include "3rdparty/pocketmod/pocketmod.h" #include "joey.h" #include "jPixBuf.h" -static SDL_Surface *_jlScreen = NULL; +#define AUDIO_FORMAT AUDIO_S16 +#define AUDIO_FREQUENCY 44100 +#define AUDIO_CHANNELS 2 + +#define BORDER_WIDTH 20 + + +typedef struct { + uint8_t *data; + uint32_t len; +} jlPlatformSoundT; + +typedef struct jlSoundPlayingS { + jlPlatformSoundT *sound; + jlSoundChannelE channel; // Corresponds to IIgs 4soniq channel definitions. + jbyte volume; // 0 = silent, 255 = loud. + uint8_t *buffer; + uint32_t len; + struct jlSoundPlayingS *next; +} jlSoundPlayingT; + +typedef struct { + char *data; + size_t size; +} jlPlatformModT; + + +static jbyte _jlBorderRGBs[16][3] = { + { 0x00, 0x00, 0x00 }, // Black + { 0xdd, 0x00, 0x33 }, // Deep Red + { 0x00, 0x00, 0x99 }, // Deep Blue + { 0xdd, 0x22, 0xdd }, // Purple + { 0x00, 0x77, 0x22 }, // Dark Green + { 0x55, 0x55, 0x55 }, // Dark Gray + { 0x22, 0x22, 0xff }, // Medium Blue + { 0x66, 0xaa, 0xff }, // Light Blue + { 0x88, 0x55, 0x00 }, // Brown + { 0xff, 0x66, 0x00 }, // Orange + { 0xaa, 0xaa, 0xaa }, // Light Gray + { 0xff, 0x99, 0x88 }, // Pink + { 0x11, 0xdd, 0x00 }, // Green + { 0xff, 0xff, 0x00 }, // Yellow + { 0x44, 0xff, 0x99 }, // Acquamarine + { 0xff, 0xff, 0xff } // White +}; + +static SDL_Surface *_jlWindow = NULL; +static SDL_Surface *_jlTexture = NULL; +static jbool _jlIsRunning = jtrue; +static jint16 _jlNumKeysDown = 0; +static char _jlLastKey = 0; +static SDL_Joystick **_jlControllers = NULL; +static jint16 _jlControllerCount = 0; +static juint16 _jlTimerValue = 0; +static SDL_TimerID _jlTimerId = 0; +static pocketmod_context _jlModContext; +static SDL_AudioSpec _jlAudioFormat; +static jbool _jlModPlaying = jfalse; +static jlPlatformModT *_jlModCurrent = NULL; +static jbyte _jlModVolume = 255; +static jlSoundPlayingT *_jlSoundList = NULL; +static jint16 _jlWindowZoom = 2; + + +static Uint32 _jlUtilTimer(Uint32 interval, void *param); +static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes); + + +extern jbool _jlSwapChannels; + + +/* +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + +Uint32 _jlGetPixel(SDL_Surface *surface, int x, int y) { + + int bpp = surface->format->BytesPerPixel; + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + return *p; + break; + + case 2: + // Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86. + return *(Uint16 *)p; + break; + + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]); + else + return (Uint32)(p[0] | p[1] << 8 | p[2] << 16); + break; + + case 4: + // Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86. + return *(Uint32 *)p; + break; + + default: + return 0; + } +} + + +void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) { + + int bpp = surface->format->BytesPerPixel; + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + *p = (Uint8)pixel; + break; + + case 2: + *(Uint16 *)p = (Uint16)pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } +} + +#pragma GCC diagnostic pop +*/ + + +static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) { + int i = 0; + jlSoundPlayingT *sound = NULL; + jlSoundPlayingT *prev = NULL; + jlSoundPlayingT *temp = NULL; + uint32_t length = 0; + int16_t *data = NULL; + int16_t *out = NULL; + float adjust = 1; + float mod[2]; + int32_t work = 0; + jbool isRight = jfalse; + + + (void)userdata; + + // Silence buffer. + memset(buffer, 0, bytes); + + // Load in music. + if (_jlModPlaying) { + // Adjust volume. Convert to S16 by multiplying rendered values by 32767. + adjust = ((100.0 / 255.0) * (float)_jlModVolume) * 0.01 * 32767; + out = (int16_t *)buffer; + while (i < bytes) { + i += pocketmod_render(&_jlModContext, mod, sizeof(float[2])); + out[i] *= adjust; + out[i + 1] *= adjust; + } + } + + // Mix in sounds. + // Just adding samples together seems fine for floating point data. + // For integers, we need to clamp. + if (_jlSoundList) { + sound = _jlSoundList; + while (sound) { + // Find length of data to mix. + length = ((uint32_t)bytes > sound->len) ? sound->len : (uint32_t)bytes; + // Adjust volume. + adjust = ((100.0 / 255.0) * (float)sound->volume) * 0.01; + // Point at sample data. + data = (int16_t *)sound->buffer; + out = (int16_t *)buffer; + for (i=0; i<(int)(length / sizeof(int16_t)); i+=2) { + // Combine channels into a mono sample. + work = (data[i] + data[i + 1]) * 0.5; + // Determine channel. + isRight = (sound->channel & 0x01); + if (_jlSwapChannels) isRight = !isRight; + if (isRight) { + // Place sound in the right channel. + out[i + 1] += (work * adjust); + } else { + // Place sound in left channel. + out[i] += (work * adjust); + } + } + sound->buffer += length; + sound->len -= length; + // Are we done with this sound? + if (sound->len == 0) { + if (prev) { + prev->next = sound->next; + } else { + _jlSoundList = sound->next; + } + temp = sound; + sound = sound->next; + jlFree(temp); + } else { + prev = sound; + sound = sound->next; + } + } + } +} void jlDisplayPresent(void) { + int i = 0; + int j = 0; + Uint16 *pixels = NULL; + SDL_Rect r; + + jlUtilIdle(); + + // Render 4 bit copy to proper pixel format. + // This extra step preserves palette effects. + SDL_LockSurface(_jlTexture); + pixels = (Uint16 *)_jlTexture->pixels; + for (int y=0; y<200; y++) { + for (int x=0; x<160; x++) { + // We decode this R/L instead of L/R for some reason. NO idea why yet. Endians? + pixels[i++] = SDL_MapRGBA(_jlTexture->format, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].r * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].g * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].b * 16, + 255); + pixels[i++] = SDL_MapRGBA(_jlTexture->format, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].r * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].g * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].b * 16, + 255); + j++; + } + } + SDL_UnlockSurface(_jlTexture); + + // Border. + SDL_FillRect(_jlWindow, NULL, SDL_MapRGB(_jlWindow->format, _jlBorderRGBs[_jlBorderColor][0], _jlBorderRGBs[_jlBorderColor][1], _jlBorderRGBs[_jlBorderColor][2])); + + // Display. + r.x = _jlWindowZoom * BORDER_WIDTH; + r.y = _jlWindowZoom * BORDER_WIDTH; + r.w = 320 * _jlWindowZoom; + r.h = 200 * _jlWindowZoom; + SDL_BlitSurface(_jlTexture, NULL, _jlWindow, &r); + SDL_Flip(_jlWindow); } -jint16 jlGameGetAxis(byte which) { - (void)which; +jint16 jlGameGetAxis(jbyte which) { + uint8_t axis; + short int unscaled; + short int max = SHRT_MIN; + short int min = SHRT_MAX; + int x; - return 0; + jlUtilIdle(); + + if (which == 0) { + axis = 0; + } else { + axis = 1; + } + + for (x=0; x<_jlControllerCount; x++) { + unscaled = SDL_JoystickGetAxis(_jlControllers[x], axis); + if (unscaled > 0) { + if (unscaled > max) { + max = unscaled; + } + } else { + if (unscaled < min) { + min = unscaled; + } + } + } + + return (-min > max ? min : max) / 256; } -bool jlGameGetButton(byte which) { - (void)which; +jbool jlGameGetButton(jbyte which) { + uint8_t button; + int x; + jbool pressed = jfalse; - return false; + jlUtilIdle(); + + if (which == 0) { + button = 0; + } else { + button = 1; + } + + for (x=0; x<_jlControllerCount; x++) { + if (SDL_JoystickGetButton(_jlControllers[x], button)) { + pressed = jtrue; + break; + } + } + + return pressed; } -bool jlKeyPressed(void) { - return false; +jbool jlKeyPressed(void) { + jlUtilIdle(); + return (_jlNumKeysDown > 0); } char jlKeyRead(void) { - return 0; + jlUtilIdle(); + return _jlLastKey; +} + + +void jlModContinue(void) { + if (_jlModCurrent) _jlModPlaying = jtrue; +} + + +void jlModFree(jlModT *mod) { + jlPlatformModT *m = (jlPlatformModT *)mod; + + // Is this the current mod? + if (m == _jlModCurrent) { + // Is it playing? + if (_jlModPlaying) { + // Lock and wait for current mod to stop playing. + SDL_LockAudio(); + jlModStop(); + SDL_UnlockAudio(); + } + _jlModCurrent = NULL; + } + + // Free loaded song. + if (m->data) { + jlFree(m->data); + jlFree(m); + } +} + + +jbool jlModIsPlaying(void) { + return _jlModPlaying; +} + + +jbool _jlModLoad(jlModT **mod, char *filename) { + FILE *in = NULL; + jlPlatformModT *m = NULL; + jbool result = jfalse; + + // New MOD. + m = (jlPlatformModT *)jlMalloc(sizeof(jlPlatformModT)); + if (m) { + // Load MOD. + in = fopen(jlUtilMakePathname(filename, "mod"), "rb"); + if (in) { + fseek(in, 0, SEEK_END); + m->size = ftell(in); + rewind(in); + m->data = (char *)jlMalloc(m->size); + if (m->data) { + fread(m->data, m->size, 1, in); + *mod = m; + result = jtrue; + } + fclose(in); + } else { + jlFree(m); + } + } + + return result; +} + + +void jlModPause(void) { + if (_jlModCurrent) _jlModPlaying = jfalse; +} + + +void jlModPlay(jlModT *mod) { + jlPlatformModT *m = (jlPlatformModT *)mod; + + // Stop any currently playing MOD. + if (_jlModPlaying) jlModStop(); + + // Start playback. + if (pocketmod_init(&_jlModContext, m->data, m->size, _jlAudioFormat.freq)) { + _jlModPlaying = jtrue; + _jlModCurrent = m; + } else { + _jlModPlaying = jfalse; + _jlModCurrent = NULL; + } +} + + +void jlModStop(void) { + _jlModPlaying = jfalse; + _jlModCurrent = NULL; +} + + +void jlModVolume(jbyte volume) { + _jlModVolume = volume; } void jlSoundFree(jlSoundT *sound) { - (void)sound; + jlPlatformSoundT *s = (jlPlatformSoundT *)sound; + + if (s != NULL) { + // Stop playing this sound. + jlSoundStop(s); + // Free it. + if (s->data != NULL) jlFree(s->data); + jlFree(s); + } } -bool jlSoundIsPlaying(jlSoundT *sound) { - (void)sound; +jbool jlSoundIsPlaying(jlSoundT *sound) { + jlPlatformSoundT *s = (jlPlatformSoundT *)sound; + jlSoundPlayingT *cur = _jlSoundList; - return false; -} + // Are we being played? + SDL_LockAudio(); + while (cur) { + if (cur->sound == s) { + // Yep + SDL_UnlockAudio(); + return jtrue; + } + cur = cur->next; + } + SDL_UnlockAudio(); - -bool _jlSoundLoad(jlSoundT **sound, char *filename) { - (void)sound; - (void)filename; - - return false; -} - - -void jlSoundMusicContinue(void) { -} - - -bool jlSoundMusicIsPlaying(void) { - return false; -} - - -void jlSoundMusicPause(void) { -} - - -void jlSoundMusicPlay(char *name) { - (void)name; -} - - -void jlSoundMusicStop(void) { -} - - -void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume) { - (void)sound; -} - - -void jlUtilIdle(void) { -} - - -jbool jlUtilMustExit(void) { return jfalse; } +jbool _jlSoundLoad(jlSoundT **sound, char *filename) { + SDL_AudioSpec wave; + SDL_AudioCVT cvt; + uint8_t *data; + uint32_t len; + jlPlatformSoundT *soundPlatform = NULL; + jbool result = jfalse; + + soundPlatform = (jlPlatformSoundT *)jlMalloc(sizeof(jlPlatformSoundT)); + if (soundPlatform) { + *sound = soundPlatform; + if (SDL_LoadWAV(jlUtilMakePathname(filename, "wav"), &wave, &data, &len)) { + // Do we need to convert to our internal format? + SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_FORMAT, AUDIO_CHANNELS, AUDIO_FREQUENCY); + if (cvt.needed) { + // Yes! Convert audio. + cvt.buf = (jbyte *)jlMalloc(len * cvt.len_mult); + if (cvt.buf) { + memcpy(cvt.buf, data, len); + cvt.len = len; + SDL_ConvertAudio(&cvt); + SDL_FreeWAV(data); + soundPlatform->len = cvt.len_cvt; + soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len); + if (soundPlatform->data) { + memcpy(soundPlatform->data, cvt.buf, soundPlatform->len); + result = jtrue; + } + data = cvt.buf; // This prevents a warning about taking the address of a packed struct. + jlFree(data); + } + } else { + // No. Use as-is. + soundPlatform->len = len; + soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len); + if (soundPlatform->data) { + memcpy(&soundPlatform->data, data, soundPlatform->len); + result = jtrue; + } + SDL_FreeWAV(data); + } + } + } + + if (!result && soundPlatform) jlFree(soundPlatform); + + return result; +} + + +void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume) { + jlSoundPlayingT *sp = NULL; + + sp = (jlSoundPlayingT *)jlMalloc(sizeof(jlSoundPlayingT)); + if (sp) { + SDL_LockAudio(); + sp->sound = sound; + sp->channel = channel; + sp->volume = volume; + sp->buffer = sp->sound->data; + sp->len = sp->sound->len; + sp->next = _jlSoundList; + _jlSoundList = sp; + SDL_UnlockAudio(); + } +} + + +void jlSoundStop(jlSoundT *sound) { + jlPlatformSoundT *s = (jlPlatformSoundT *)sound; + jlSoundPlayingT *prev = NULL; + jlSoundPlayingT *temp = NULL; + jlSoundPlayingT *cur = _jlSoundList; + + // Are we being played? + SDL_LockAudio(); + while (cur) { + if (cur->sound == s) { + if (prev) { + prev->next = cur->next; + } else { + _jlSoundList = cur->next; + } + temp = cur; + cur = cur->next; + jlFree(temp); + } else { + prev = cur; + cur = cur->next; + } + } + SDL_UnlockAudio(); +} + + +char _jlUtilIdleCheckKey(int sym) { + + char key = 0; + SDLMod mod = 0; + + // Do we even care about this key? + if ((sym < 8) || (sym > 127)) { + key = 0; + } else { + mod = SDL_GetModState(); + key = (char)sym; + // Map LF to CR + if (key == 10) { + key = 13; + } + // Map DEL to BS + if (key == 127) { + key = 8; + } + // Convert to uppercase if needed + if ((mod == KMOD_SHIFT) && (key >= 'a') && (key <= 'z')) { + key -= 32; + } + // Is this outside the range we care about? + if ((key < ' ') || (key > '~')) { + if ((key != 8) && (key != 13) && (key != 27)) { + key = 0; + } + } + } + + return key; +} + + +void jlUtilIdle(void) { + SDL_Event event; + + SDL_JoystickUpdate(); + + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_KEYDOWN: + // Track keydown/keyup and remember last key pressed. + switch (event.key.keysym.sym) { + case SDLK_F1: + _jlWindowZoom = 1; + _jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE); + break; + + case SDLK_F2: + _jlWindowZoom = 2; + _jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE); + break; + + case SDLK_F3: + _jlWindowZoom = 3; + _jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE); + break; + + default: + _jlLastKey = _jlUtilIdleCheckKey(event.key.keysym.sym); + if (_jlLastKey > 0) { + //***FIX*** + //_jlNumKeysDown++; + _jlNumKeysDown = 1; + } + } + break; + + case SDL_KEYUP: + if (_jlUtilIdleCheckKey(event.key.keysym.sym) > 0) { + //***FIX*** + //_jlNumKeysDown--; + _jlNumKeysDown = 0; + } + break; + + case SDL_QUIT: + _jlIsRunning = jfalse; + break; + } + + SDL_Delay(1); + } + + //printf("Keys down: %d\n", _jlNumKeysDown); + + SDL_Delay(1); +} + + +jbool jlUtilMustExit(void) { + return !_jlIsRunning; +} + + +static Uint32 _jlUtilTimer(Uint32 interval, void *param) { + (void)param; + _jlTimerValue++; + return(interval); +} + + juint16 jlUtilTimer(void) { - return 0; + return _jlTimerValue; } void jlUtilTitleSet(char *title) { -#ifdef JOEY_LINUX - SDL_WM_SetCaption(title, NULL); -#endif + SDL_WM_SetCaption(title, NULL); // ***TODO*** Needs icon. } -int main(void) { +int main(int argc, char *argv[]) { - Uint32 vflags; - int mflags; - int result; + int joysticks; + int x; + + (void)argc; + (void)argv; // Start low-level tools if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { jlUtilDie(SDL_GetError()); } - // Start audio mixer & music system. - mflags = MIX_INIT_MOD; - result = Mix_Init(mflags); - if ((result & mflags) != mflags) { - jlUtilDie(Mix_GetError()); - } - if (Mix_OpenAudio(11025, AUDIO_U8, 1, 1024) == -1) { - jlUtilDie(Mix_GetError()); - } + // Set up controllers + joysticks = SDL_NumJoysticks(); + _jlControllers = (SDL_Joystick **)jlMalloc(sizeof(SDL_Joystick *) * (unsigned long)joysticks); + for (x=0; x