Download unify/backends/sdl/sdl_backend.cpp from Snapkitty/repoverse: direct link, hf CLI and curl.
- Browser
- Download file 8.82 kB
-
https://huggingface.co/Snapkitty/repoverse/resolve/main/unify/backends/sdl/sdl_backend.cpp
- Command line
-
hf download hf://Snapkitty/repoverse/unify/backends/sdl/sdl_backend.cpp
-
curl -L -o sdl_backend.cpp https://huggingface.co/Snapkitty/repoverse/resolve/main/unify/backends/sdl/sdl_backend.cpp
8.82 kB
| namespace unify { | |
| // ------------------------------------------------------------------------------ audio | |
| void SdlAudioDevice::callback(void* user, uint8_t* stream, int len) { | |
| auto* self = static_cast<SdlAudioDevice*>(user); | |
| self->mixer_->render(reinterpret_cast<float*>(stream), uint32_t(len / int(sizeof(float) * 2))); | |
| } | |
| bool SdlAudioDevice::start(Mixer& mixer) { | |
| mixer_ = &mixer; | |
| SDL_AudioSpec want{}, have{}; | |
| want.freq = int(mixer.device_rate()); | |
| want.format = AUDIO_F32SYS; | |
| want.channels = 2; | |
| want.samples = 512; | |
| want.callback = &SdlAudioDevice::callback; | |
| want.userdata = this; | |
| device_ = SDL_OpenAudioDevice(nullptr, 0, &want, &have, 0); | |
| if (!device_) { UNIFY_LOG_WARN("AUDIO", "SDL audio unavailable: %s", SDL_GetError()); return false; } | |
| SDL_PauseAudioDevice(device_, 0); | |
| UNIFY_LOG_INFO("AUDIO", "SDL audio: %d Hz, %d-frame buffer", have.freq, have.samples); | |
| return true; | |
| } | |
| void SdlAudioDevice::stop() { | |
| if (device_) { SDL_CloseAudioDevice(device_); device_ = 0; } | |
| } | |
| // ------------------------------------------------------------------------------ backend | |
| AudioDevice& SdlBackend::audio_device() { return audio_ok_ ? static_cast<AudioDevice&>(audio_) : null_audio_; } | |
| bool SdlBackend::init(const EngineConfig& config, std::string& error) { | |
| SDL_SetMainReady(); | |
| if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { error = SDL_GetError(); return false; } | |
| if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { | |
| UNIFY_LOG_WARN("AUDIO", "SDL audio subsystem failed (%s); using the null device", SDL_GetError()); | |
| audio_ok_ = false; | |
| } else { | |
| // Probe the device now so the engine starts the right one. | |
| SDL_AudioSpec want{}, have{}; | |
| want.freq = 44100; want.format = AUDIO_F32SYS; want.channels = 2; want.samples = 512; | |
| want.callback = [](void*, uint8_t* s, int n) { SDL_memset(s, 0, size_t(n)); }; | |
| SDL_AudioDeviceID probe = SDL_OpenAudioDevice(nullptr, 0, &want, &have, 0); | |
| if (!probe) { audio_ok_ = false; UNIFY_LOG_WARN("AUDIO", "no audio device (%s); using the null device", SDL_GetError()); } | |
| else SDL_CloseAudioDevice(probe); | |
| } | |
| width_ = config.width; | |
| height_ = config.height; | |
| window_ = SDL_CreateWindow(config.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width_ * 2, height_ * 2, | |
| SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | |
| if (!window_) { error = SDL_GetError(); return false; } | |
| renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_PRESENTVSYNC); | |
| if (!renderer_) renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_SOFTWARE); | |
| if (!renderer_) { error = SDL_GetError(); return false; } | |
| SDL_RenderSetLogicalSize(renderer_, width_, height_); // integer-scaled pixel art, letterboxed | |
| SDL_RenderSetIntegerScale(renderer_, SDL_TRUE); | |
| texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width_, height_); | |
| if (!texture_) { error = SDL_GetError(); return false; } | |
| for (int i = 0; i < SDL_NumJoysticks(); i++) | |
| if (SDL_IsGameController(i)) { pad_ = SDL_GameControllerOpen(i); break; } | |
| perf_freq_ = SDL_GetPerformanceFrequency(); | |
| perf_start_ = SDL_GetPerformanceCounter(); | |
| initialized_ = true; | |
| UNIFY_LOG_INFO("BOOT", "SDL %s video driver, window %dx%d", SDL_GetCurrentVideoDriver(), width_ * 2, height_ * 2); | |
| return true; | |
| } | |
| uint64_t SdlBackend::now_ns() { | |
| uint64_t t = SDL_GetPerformanceCounter() - perf_start_; | |
| return uint64_t((__uint128_t(t) * 1000000000u) / perf_freq_); | |
| } | |
| static uint16_t map_key(SDL_Keycode k) { | |
| switch (k) { | |
| case SDLK_SPACE: return key::Space; | |
| case SDLK_LEFT: return key::Left; | |
| case SDLK_RIGHT: return key::Right; | |
| case SDLK_UP: return key::Up; | |
| case SDLK_DOWN: return key::Down; | |
| case SDLK_RETURN: return key::Enter; | |
| case SDLK_ESCAPE: return key::Escape; | |
| case SDLK_F1: return key::F1; | |
| case SDLK_F2: return key::F2; | |
| case SDLK_F3: return key::F3; | |
| case SDLK_F5: return key::F5; | |
| case SDLK_F9: return key::F9; | |
| case SDLK_LSHIFT: return key::LShift; | |
| default: return (k >= 'a' && k <= 'z') ? uint16_t(k) : 0; | |
| } | |
| } | |
| static uint16_t map_pad(uint8_t b) { | |
| switch (b) { | |
| case SDL_CONTROLLER_BUTTON_A: return key::PadA; | |
| case SDL_CONTROLLER_BUTTON_B: return key::PadB; | |
| case SDL_CONTROLLER_BUTTON_X: return key::PadX; | |
| case SDL_CONTROLLER_BUTTON_Y: return key::PadY; | |
| case SDL_CONTROLLER_BUTTON_START: return key::PadStart; | |
| case SDL_CONTROLLER_BUTTON_BACK: return key::PadBack; | |
| case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return key::PadLeft; | |
| case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return key::PadRight; | |
| case SDL_CONTROLLER_BUTTON_DPAD_UP: return key::PadUp; | |
| case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return key::PadDown; | |
| default: return 0; | |
| } | |
| } | |
| void SdlBackend::poll(InputSystem& input, bool& quit) { | |
| SDL_Event ev; | |
| while (SDL_PollEvent(&ev)) { | |
| InputEvent e; | |
| e.timestamp_ns = now_ns(); // stamped on arrival: decides which simulation tick consumes it | |
| switch (ev.type) { | |
| case SDL_QUIT: quit = true; break; | |
| case SDL_KEYDOWN: case SDL_KEYUP: | |
| e.device = InputDevice::Keyboard; | |
| e.code = map_key(ev.key.keysym.sym); | |
| if (!e.code) break; | |
| e.type = ev.type == SDL_KEYUP ? InputEventType::ButtonUp : ev.key.repeat ? InputEventType::ButtonRepeat : InputEventType::ButtonDown; | |
| input.push(e); | |
| break; | |
| case SDL_MOUSEMOTION: { | |
| float lx, ly; | |
| SDL_RenderWindowToLogical(renderer_, ev.motion.x, ev.motion.y, &lx, &ly); | |
| e.device = InputDevice::Mouse; e.type = InputEventType::PointerMove; e.value = lx; e.value2 = ly; | |
| input.push(e); | |
| break; | |
| } | |
| case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: | |
| e.device = InputDevice::Mouse; | |
| e.type = ev.type == SDL_MOUSEBUTTONDOWN ? InputEventType::ButtonDown : InputEventType::ButtonUp; | |
| e.code = ev.button.button == SDL_BUTTON_LEFT ? key::MouseLeft : ev.button.button == SDL_BUTTON_RIGHT ? key::MouseRight : key::MouseMiddle; | |
| input.push(e); | |
| break; | |
| case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: | |
| e.device = InputDevice::Gamepad; | |
| e.type = ev.type == SDL_CONTROLLERBUTTONDOWN ? InputEventType::ButtonDown : InputEventType::ButtonUp; | |
| e.code = map_pad(ev.cbutton.button); | |
| if (e.code) input.push(e); | |
| break; | |
| case SDL_CONTROLLERAXISMOTION: | |
| if (ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX || ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY) { | |
| e.device = InputDevice::Gamepad; e.type = InputEventType::Axis; | |
| e.code = ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX ? key::PadAxisLeftX : key::PadAxisLeftY; | |
| e.value = ev.caxis.value / 32767.0f; | |
| input.push(e); | |
| } | |
| break; | |
| case SDL_CONTROLLERDEVICEADDED: | |
| if (!pad_) pad_ = SDL_GameControllerOpen(ev.cdevice.which); | |
| break; | |
| case SDL_FINGERDOWN: case SDL_FINGERUP: case SDL_FINGERMOTION: | |
| e.device = InputDevice::Touch; | |
| e.type = InputEventType::PointerMove; e.value = ev.tfinger.x * float(width_); e.value2 = ev.tfinger.y * float(height_); | |
| input.push(e); | |
| if (ev.type != SDL_FINGERMOTION) { | |
| e.type = ev.type == SDL_FINGERDOWN ? InputEventType::ButtonDown : InputEventType::ButtonUp; | |
| e.code = key::Touch0; | |
| input.push(e); | |
| } | |
| break; | |
| default: break; | |
| } | |
| } | |
| } | |
| void SdlBackend::present(const RenderTarget& frame) { | |
| SDL_UpdateTexture(texture_, nullptr, frame.color.pixels.data(), frame.width() * 4); | |
| SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255); | |
| SDL_RenderClear(renderer_); | |
| SDL_RenderCopy(renderer_, texture_, nullptr, nullptr); | |
| SDL_RenderPresent(renderer_); | |
| } | |
| void SdlBackend::shutdown() { | |
| if (!initialized_) return; | |
| audio_.stop(); | |
| null_audio_.stop(); | |
| if (pad_) SDL_GameControllerClose(pad_); | |
| if (texture_) SDL_DestroyTexture(texture_); | |
| if (renderer_) SDL_DestroyRenderer(renderer_); | |
| if (window_) SDL_DestroyWindow(window_); | |
| SDL_Quit(); | |
| initialized_ = false; | |
| } | |
| } // namespace unify | |