2023-02-18 04:34:40 +01:00
|
|
|
module;
|
|
|
|
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
|
|
|
export module app.sdl;
|
|
|
|
|
|
|
|
import app;
|
|
|
|
|
|
|
|
import core;
|
|
|
|
import core.image;
|
|
|
|
import core.sequence;
|
2023-02-18 20:40:12 +01:00
|
|
|
import core.lalgebra;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
struct bundled_file_store : public app::file_store {
|
|
|
|
void read_file(app::path const & file_path, core::callable<void(core::readable const &)> const & then) override {
|
2023-02-18 20:40:12 +01:00
|
|
|
constexpr core::slice<char const> path_prefix = "./";
|
|
|
|
constexpr core::usize path_max = 512;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
if ((file_path.size() + path_prefix.length) > path_max) core::unreachable();
|
|
|
|
|
|
|
|
core::stack<char, path_max> path_buffer{&core::null_allocator()};
|
|
|
|
|
|
|
|
if (path_buffer.append("./") != core::append_result::ok) core::unreachable();
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
// File path is guaranteed to be null-terminated.
|
2023-02-18 20:40:12 +01:00
|
|
|
if (path_buffer.append(file_path.as_slice()) != core::append_result::ok)
|
|
|
|
core::unreachable();
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
SDL_RWops * rw_ops = ::SDL_RWFromFile(path_buffer.begin(), "r");
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
if (rw_ops == nullptr) return;
|
|
|
|
|
|
|
|
then([rw_ops](core::slice<uint8_t> const & buffer) -> size_t {
|
|
|
|
return ::SDL_RWread(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length);
|
|
|
|
});
|
|
|
|
|
|
|
|
::SDL_RWclose(rw_ops);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sdl_allocator : public core::allocator {
|
|
|
|
core::u8 * reallocate(core::u8 * maybe_allocation, core::usize requested_size) override {
|
|
|
|
return reinterpret_cast<core::u8 *>(::SDL_malloc(requested_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void deallocate(void * allocation) override {
|
|
|
|
::SDL_free(allocation);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sdl_system : public app::system {
|
|
|
|
private:
|
|
|
|
::SDL_Event sdl_event;
|
|
|
|
|
|
|
|
sdl_allocator allocator;
|
|
|
|
|
|
|
|
bundled_file_store bundled_store;
|
|
|
|
|
|
|
|
public:
|
|
|
|
sdl_system() :
|
|
|
|
sdl_event{0},
|
|
|
|
allocator{} {}
|
|
|
|
|
|
|
|
bool poll() override {
|
|
|
|
while (::SDL_PollEvent(&this->sdl_event) != 0) {
|
|
|
|
switch (this->sdl_event.type) {
|
|
|
|
case SDL_QUIT: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
app::file_store & bundle() override {
|
|
|
|
return this->bundled_store;
|
|
|
|
}
|
|
|
|
|
|
|
|
void log(app::log_level level, core::slice<char const> const & message) override {
|
|
|
|
core::i32 const length = static_cast<core::i32>(
|
|
|
|
core::min(message.length, static_cast<size_t>(core::i32_max)));
|
|
|
|
|
|
|
|
::SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
|
|
|
|
SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
core::allocator & thread_safe_allocator() override {
|
|
|
|
return this->allocator;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sdl_graphics : public app::graphics {
|
|
|
|
static constexpr core::usize title_maximum = 128;
|
|
|
|
|
|
|
|
core::u32 title_length;
|
|
|
|
|
|
|
|
char title_buffer[title_maximum];
|
|
|
|
|
|
|
|
::SDL_Window * sdl_window = nullptr;
|
|
|
|
|
|
|
|
::SDL_Renderer * sdl_renderer = nullptr;
|
|
|
|
|
|
|
|
sdl_graphics(core::slice<char const> const & title) {
|
|
|
|
this->retitle(title);
|
|
|
|
}
|
|
|
|
|
|
|
|
void render(canvas & source_canvas) override {
|
|
|
|
if (this->sdl_renderer != nullptr) {
|
|
|
|
SDL_SetRenderDrawColor(this->sdl_renderer, source_canvas.background_color.to_r8(),
|
|
|
|
source_canvas.background_color.to_g8(), source_canvas.background_color.to_b8(),
|
|
|
|
source_canvas.background_color.to_a8());
|
|
|
|
|
|
|
|
SDL_RenderClear(this->sdl_renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void retitle(core::slice<char const> const & title) override {
|
|
|
|
this->title_length = core::min(title.length, title_maximum - 1);
|
|
|
|
|
|
|
|
for (core::usize i = 0; i < this->title_length; i += 1) title_buffer[i] = title[i];
|
|
|
|
|
|
|
|
for (core::usize i = this->title_length; i < title_maximum; i += 1) title_buffer[i] = 0;
|
|
|
|
|
|
|
|
if (this->sdl_window != nullptr) ::SDL_SetWindowTitle(this->sdl_window, title_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
app::graphics::show_error show(core::u16 physical_width, core::u16 physical_height) override {
|
|
|
|
if (this->sdl_window == nullptr) {
|
|
|
|
constexpr int sdl_windowpos = SDL_WINDOWPOS_UNDEFINED;
|
|
|
|
constexpr core::u32 sdl_windowflags = 0;
|
|
|
|
|
|
|
|
this->sdl_window = ::SDL_CreateWindow(title_buffer, sdl_windowpos, sdl_windowpos,
|
|
|
|
static_cast<int>(physical_width), static_cast<int>(physical_height),
|
|
|
|
sdl_windowflags);
|
|
|
|
|
|
|
|
if (this->sdl_window == nullptr) return show_error::out_of_memory;
|
|
|
|
} else {
|
|
|
|
::SDL_ShowWindow(this->sdl_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->sdl_renderer == nullptr) {
|
|
|
|
constexpr core::u32 sdl_rendererflags = 0;
|
|
|
|
|
|
|
|
this->sdl_renderer = ::SDL_CreateRenderer(this->sdl_window, -1, sdl_rendererflags);
|
|
|
|
|
|
|
|
if (this->sdl_renderer == nullptr) return show_error::out_of_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
return show_error::none;
|
|
|
|
}
|
|
|
|
|
|
|
|
void present() override {
|
|
|
|
if (this->sdl_renderer != nullptr) {
|
|
|
|
::SDL_RenderPresent(this->sdl_renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export namespace app {
|
|
|
|
int display(core::slice<char const> const & title,
|
|
|
|
core::callable<int(app::system &, app::graphics &)> const & run) {
|
|
|
|
|
|
|
|
sdl_system system;
|
|
|
|
sdl_graphics graphics(title);
|
|
|
|
|
|
|
|
return run(system, graphics);
|
|
|
|
}
|
|
|
|
}
|