diff --git a/CMakeLists.txt b/CMakeLists.txt index d1953fc..0516abd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,11 +43,11 @@ add_library(blah src/streams/memorystream.cpp src/streams/stream.cpp - src/internal/graphics_backend_gl.cpp - src/internal/graphics_backend_d3d11.cpp - src/internal/graphics_backend_dummy.cpp - src/internal/platform_backend_sdl2.cpp - src/internal/platform_backend_win32.cpp + src/internal/graphics_gl.cpp + src/internal/graphics_d3d11.cpp + src/internal/graphics_dummy.cpp + src/internal/platform_sdl2.cpp + src/internal/platform_win32.cpp ) target_include_directories(blah diff --git a/src/app.cpp b/src/app.cpp index f91e525..ee41b30 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -3,9 +3,9 @@ #include #include #include -#include "internal/platform_backend.h" -#include "internal/graphics_backend.h" -#include "internal/input_backend.h" +#include "internal/platform.h" +#include "internal/graphics.h" +#include "internal/input.h" #ifdef __EMSCRIPTEN__ #include @@ -44,7 +44,7 @@ namespace // TODO: allow a non-fixed step update? { u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); - u64 time_curr = PlatformBackend::ticks(); + u64 time_curr = Platform::ticks(); u64 time_diff = time_curr - time_last; time_last = time_curr; time_accumulator += time_diff; @@ -53,9 +53,9 @@ namespace while (time_accumulator < time_target) { int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000); - PlatformBackend::sleep(milliseconds); + Platform::sleep(milliseconds); - time_curr = PlatformBackend::ticks(); + time_curr = Platform::ticks(); time_diff = time_curr - time_last; time_last = time_curr; time_accumulator += time_diff; @@ -88,10 +88,10 @@ namespace Time::previous_seconds = Time::seconds; Time::seconds += Time::delta; - InputBackend::update_state(); - PlatformBackend::update(Input::state); - InputBackend::update_bindings(); - GraphicsBackend::update(); + Input::update_state(); + Platform::update(Input::state); + Input::update_bindings(); + Graphics::update(); if (app_config.on_update != nullptr) app_config.on_update(); @@ -100,13 +100,13 @@ namespace // render { - GraphicsBackend::before_render(); + Graphics::before_render(); if (app_config.on_render != nullptr) app_config.on_render(); - GraphicsBackend::after_render(); - PlatformBackend::present(); + Graphics::after_render(); + Platform::present(); } } @@ -126,31 +126,31 @@ bool App::run(const Config* c) app_is_exiting = false; // initialize the system - if (!PlatformBackend::init(app_config)) + if (!Platform::init(app_config)) { Log::error("Failed to initialize Platform module"); return false; } // initialize graphics - if (!GraphicsBackend::init()) + if (!Graphics::init()) { Log::error("Failed to initialize Graphics module"); return false; } // input - InputBackend::init(); + Input::init(); // startup if (app_config.on_startup != nullptr) app_config.on_startup(); - time_last = PlatformBackend::ticks(); + time_last = Platform::ticks(); time_accumulator = 0; // display window - PlatformBackend::ready(); + Platform::ready(); // Begin main loop // Emscripten requires the main loop be separated into its own call @@ -166,8 +166,8 @@ bool App::run(const Config* c) if (app_config.on_shutdown != nullptr) app_config.on_shutdown(); - GraphicsBackend::shutdown(); - PlatformBackend::shutdown(); + Graphics::shutdown(); + Platform::shutdown(); // clear static state app_is_running = false; @@ -195,46 +195,46 @@ const Config& App::config() const char* App::path() { - return PlatformBackend::app_path(); + return Platform::app_path(); } const char* App::user_path() { - return PlatformBackend::user_path(); + return Platform::user_path(); } const char* App::get_title() { - return PlatformBackend::get_title(); + return Platform::get_title(); } void App::set_title(const char* title) { - PlatformBackend::set_title(title); + Platform::set_title(title); } Point App::get_position() { Point result; - PlatformBackend::get_position(&result.x, &result.y); + Platform::get_position(&result.x, &result.y); return result; } void App::set_position(Point point) { - PlatformBackend::set_position(point.x, point.y); + Platform::set_position(point.x, point.y); } Point App::get_size() { Point result; - PlatformBackend::get_size(&result.x, &result.y); + Platform::get_size(&result.x, &result.y); return result; } void App::set_size(Point point) { - PlatformBackend::set_size(point.x, point.y); + Platform::set_size(point.x, point.y); } int App::width() @@ -250,35 +250,35 @@ int App::height() int App::draw_width() { int w, h; - PlatformBackend::get_draw_size(&w, &h); + Platform::get_draw_size(&w, &h); return w; } int App::draw_height() { int w, h; - PlatformBackend::get_draw_size(&w, &h); + Platform::get_draw_size(&w, &h); return h; } float App::content_scale() { - return PlatformBackend::get_content_scale(); + return Platform::get_content_scale(); } void App::fullscreen(bool enabled) { - PlatformBackend::set_fullscreen(enabled); + Platform::set_fullscreen(enabled); } Renderer App::renderer() { - return GraphicsBackend::renderer(); + return Graphics::renderer(); } const RendererFeatures& Blah::App::renderer_features() { - return GraphicsBackend::features(); + return Graphics::features(); } namespace @@ -313,7 +313,7 @@ namespace void clear(Color color, float depth, u8 stencil, ClearMask mask) override { - GraphicsBackend::clear_backbuffer(color, depth, stencil, mask); + Graphics::clear_backbuffer(color, depth, stencil, mask); } }; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 03e2b7b..9322513 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -1,37 +1,37 @@ #include #include -#include "internal/platform_backend.h" +#include "internal/platform.h" using namespace Blah; FileRef File::open(const FilePath& path, FileMode mode) { - return PlatformBackend::file_open(path.cstr(), mode); + return Platform::file_open(path.cstr(), mode); } bool File::exists(const FilePath& path) { - return PlatformBackend::file_exists(path.cstr()); + return Platform::file_exists(path.cstr()); } bool File::destroy(const FilePath& path) { - return PlatformBackend::file_delete(path.cstr()); + return Platform::file_delete(path.cstr()); } bool Directory::create(const FilePath& path) { - return PlatformBackend::dir_create(path.cstr()); + return Platform::dir_create(path.cstr()); } bool Directory::exists(const FilePath& path) { - return PlatformBackend::dir_exists(path.cstr()); + return Platform::dir_exists(path.cstr()); } bool Directory::destroy(const FilePath& path) { - return PlatformBackend::dir_delete(path.cstr()); + return Platform::dir_delete(path.cstr()); } Vector Directory::enumerate(const FilePath& path, bool recursive) @@ -39,7 +39,7 @@ Vector Directory::enumerate(const FilePath& path, bool recursive) Vector list; // get files - PlatformBackend::dir_enumerate(list, path.cstr(), recursive); + Platform::dir_enumerate(list, path.cstr(), recursive); // normalize path names for (auto& it : list) @@ -50,7 +50,7 @@ Vector Directory::enumerate(const FilePath& path, bool recursive) void Directory::explore(const FilePath& path) { - PlatformBackend::dir_explore(path); + Platform::dir_explore(path); } FilePath Path::get_file_name(const FilePath& path) diff --git a/src/graphics/mesh.cpp b/src/graphics/mesh.cpp index 9c308ea..25d69d7 100644 --- a/src/graphics/mesh.cpp +++ b/src/graphics/mesh.cpp @@ -1,12 +1,12 @@ #include -#include "../internal/graphics_backend.h" +#include "../internal/graphics.h" using namespace Blah; MeshRef Mesh::create() { - return GraphicsBackend::create_mesh(); + return Graphics::create_mesh(); } VertexFormat::VertexFormat(std::initializer_list attributes, int stride) diff --git a/src/graphics/renderpass.cpp b/src/graphics/renderpass.cpp index d786e19..c72d53f 100644 --- a/src/graphics/renderpass.cpp +++ b/src/graphics/renderpass.cpp @@ -1,6 +1,6 @@ #include #include -#include "../internal/graphics_backend.h" +#include "../internal/graphics.h" using namespace Blah; @@ -86,5 +86,5 @@ void RenderPass::perform() pass.scissor = pass.scissor.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y)); // perform render - GraphicsBackend::render(pass); + Graphics::render(pass); } diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 8e82230..cdc8738 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -1,6 +1,6 @@ #include #include -#include "../internal/graphics_backend.h" +#include "../internal/graphics.h" using namespace Blah; @@ -10,7 +10,7 @@ ShaderRef Shader::create(const ShaderData& data) BLAH_ASSERT(data.fragment.length() > 0, "Must provide a Fragment Shader"); BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer() != Renderer::D3D11, "D3D11 Shaders must have hlsl_attributes assigned"); - auto shader = GraphicsBackend::create_shader(&data); + auto shader = Graphics::create_shader(&data); // validate the shader if (shader) diff --git a/src/graphics/target.cpp b/src/graphics/target.cpp index 03d3eac..3fdaab9 100644 --- a/src/graphics/target.cpp +++ b/src/graphics/target.cpp @@ -1,5 +1,5 @@ #include -#include "../internal/graphics_backend.h" +#include "../internal/graphics.h" using namespace Blah; @@ -29,7 +29,7 @@ TargetRef Target::create(int width, int height, const AttachmentFormats& texture BLAH_ASSERT(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture"); BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count"); - return GraphicsBackend::create_target(width, height, textures.data(), textures.size()); + return Graphics::create_target(width, height, textures.data(), textures.size()); } TextureRef& Target::texture(int index) diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index a614e3f..f20634f 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "../internal/graphics_backend.h" +#include "../internal/graphics.h" using namespace Blah; @@ -16,7 +16,7 @@ TextureRef Texture::create(int width, int height, TextureFormat format, unsigned BLAH_ASSERT(width > 0 && height > 0, "Texture width and height must be larger than 0"); BLAH_ASSERT((int)format > (int)TextureFormat::None && (int)format < (int)TextureFormat::Count, "Invalid texture format"); - auto tex = GraphicsBackend::create_texture(width, height, format); + auto tex = Graphics::create_texture(width, height, format); if (tex && data != nullptr) tex->set_data(data); diff --git a/src/images/font.cpp b/src/images/font.cpp index 7b2568c..8dc385b 100644 --- a/src/images/font.cpp +++ b/src/images/font.cpp @@ -5,19 +5,10 @@ using namespace Blah; -#ifdef __CLANG__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#endif - #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #include "../third_party/stb_truetype.h" -#ifdef __CLANG__ -#pragma clang diagnostic pop -#endif - namespace { String get_font_name(stbtt_fontinfo* font, int nameId) diff --git a/src/images/image.cpp b/src/images/image.cpp index c564d11..d0f74f8 100644 --- a/src/images/image.cpp +++ b/src/images/image.cpp @@ -15,18 +15,18 @@ using namespace Blah; namespace { - int Blah_STBI_Read(void* user, char* data, int size) + int blah_stbi_read(void* user, char* data, int size) { i64 read = ((Stream*)user)->read(data, size); return (int)read; } - void Blah_STBI_Skip(void* user, int n) + void blah_stbi_skip(void* user, int n) { ((Stream*)user)->seek(((Stream*)user)->position() + n); } - int Blah_STBI_Eof(void* user) + int blaH_stbi_eof(void* user) { i64 position = ((Stream*)user)->position(); i64 length = ((Stream*)user)->length(); @@ -37,7 +37,7 @@ namespace return 0; } - void Blah_STBI_Write(void* context, void* data, int size) + void blah_stbi_write(void* context, void* data, int size) { ((Stream*)context)->write((char*)data, size); } @@ -142,34 +142,30 @@ Image::~Image() dispose(); } -void Image::from_stream(Stream& stream) +bool Image::from_stream(Stream& stream) { dispose(); if (!stream.is_readable()) - { - BLAH_ASSERT(false, "Unable to load image as the Stream was not readable"); - return; - } + return false; stbi_io_callbacks callbacks; - callbacks.eof = Blah_STBI_Eof; - callbacks.read = Blah_STBI_Read; - callbacks.skip = Blah_STBI_Skip; + callbacks.eof = blaH_stbi_eof; + callbacks.read = blah_stbi_read; + callbacks.skip = blah_stbi_skip; int x, y, comps; u8* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4); if (data == nullptr) - { - BLAH_ASSERT(false, "Unable to load image as the Stream's data was not a valid image"); - return; - } + return false; m_stbi_ownership = true; pixels = (Color*)data; width = x; height = y; + + return true; } void Image::dispose() @@ -223,14 +219,8 @@ bool Image::save_png(Stream& stream) const stbi_write_force_png_filter = 0; stbi_write_png_compression_level = 0; - if (stbi_write_png_to_func(Blah_STBI_Write, &stream, width, height, 4, pixels, width * 4) != 0) + if (stbi_write_png_to_func(blah_stbi_write, &stream, width, height, 4, pixels, width * 4) != 0) return true; - else - Log::error("stbi_write_png_to_func failed"); - } - else - { - Log::error("Cannot save Image, the Stream is not writable"); } return false; @@ -260,14 +250,8 @@ bool Image::save_jpg(Stream& stream, int quality) const if (stream.is_writable()) { - if (stbi_write_jpg_to_func(Blah_STBI_Write, &stream, width, height, 4, pixels, quality) != 0) + if (stbi_write_jpg_to_func(blah_stbi_write, &stream, width, height, 4, pixels, quality) != 0) return true; - else - Log::error("stbi_write_jpg_to_func failed"); - } - else - { - Log::error("Cannot save Image, the Stream is not writable"); } return false; diff --git a/src/input.cpp b/src/input.cpp index 029e591..16ef4f5 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -4,7 +4,7 @@ #include #include #include -#include "internal/input_backend.h" +#include "internal/input.h" #include using namespace Blah; @@ -21,7 +21,7 @@ namespace InputState Blah::Input::state; InputState Blah::Input::last_state; -void InputBackend::init() +void Input::init() { g_empty_controller.name = "Disconnected"; for (int i = 0; i < Input::max_controllers; i++) @@ -34,7 +34,7 @@ void InputBackend::init() g_sticks.dispose(); } -void InputBackend::update_state() +void Input::update_state() { // cycle states Input::last_state = Input::state; @@ -70,7 +70,7 @@ void InputBackend::update_state() } } -void InputBackend::update_bindings() +void Input::update_bindings() { for (int i = 0; i < g_buttons.size(); i++) { diff --git a/src/internal/graphics_backend.h b/src/internal/graphics.h similarity index 98% rename from src/internal/graphics_backend.h rename to src/internal/graphics.h index 7620db0..ad7098d 100644 --- a/src/internal/graphics_backend.h +++ b/src/internal/graphics.h @@ -12,7 +12,7 @@ namespace Blah { // Graphics backend API used for rendering. // All rendering ends up going through here. - namespace GraphicsBackend + namespace Graphics { // Initializes the graphics backend bool init(); diff --git a/src/internal/graphics_backend_d3d11.cpp b/src/internal/graphics_d3d11.cpp similarity index 97% rename from src/internal/graphics_backend_d3d11.cpp rename to src/internal/graphics_d3d11.cpp index edde966..fb27ee3 100644 --- a/src/internal/graphics_backend_d3d11.cpp +++ b/src/internal/graphics_d3d11.cpp @@ -3,8 +3,8 @@ // TODO: // Note the D3D11 Implementation is still a work-in-progress -#include "../internal/graphics_backend.h" -#include "../internal/platform_backend.h" +#include "graphics.h" +#include "platform.h" #include #include #include @@ -673,7 +673,7 @@ namespace Blah } }; - bool GraphicsBackend::init() + bool Graphics::init() { state = D3D11(); state.last_size = Point(App::draw_width(), App::draw_height()); @@ -687,7 +687,7 @@ namespace Blah desc.SampleDesc.Quality = 0; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 1; - desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd(); + desc.OutputWindow = (HWND)Platform::d3d11_get_hwnd(); desc.Windowed = true; // Creation Flags @@ -755,12 +755,12 @@ namespace Blah return true; } - Renderer GraphicsBackend::renderer() + Renderer Graphics::renderer() { return Renderer::D3D11; } - void GraphicsBackend::shutdown() + void Graphics::shutdown() { // release cached objects for (auto& it : state.blend_cache) @@ -787,16 +787,16 @@ namespace Blah state = D3D11(); } - const RendererFeatures& GraphicsBackend::features() + const RendererFeatures& Graphics::features() { return state.features; } - void GraphicsBackend::update() + void Graphics::update() { } - void GraphicsBackend::before_render() + void Graphics::before_render() { HRESULT hr; @@ -824,13 +824,13 @@ namespace Blah } } - void GraphicsBackend::after_render() + void Graphics::after_render() { auto hr = state.swap_chain->Present(1, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain"); } - TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format) + TextureRef Graphics::create_texture(int width, int height, TextureFormat format) { auto result = new D3D11_Texture(width, height, format, false); @@ -841,12 +841,12 @@ namespace Blah return TextureRef(); } - TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachment_count) + TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachment_count) { return TargetRef(new D3D11_Target(width, height, attachments, attachment_count)); } - ShaderRef GraphicsBackend::create_shader(const ShaderData* data) + ShaderRef Graphics::create_shader(const ShaderData* data) { auto result = new D3D11_Shader(data); if (result->valid) @@ -856,12 +856,12 @@ namespace Blah return ShaderRef(); } - MeshRef GraphicsBackend::create_mesh() + MeshRef Graphics::create_mesh() { return MeshRef(new D3D11_Mesh()); } - void GraphicsBackend::render(const RenderPass& pass) + void Graphics::render(const RenderPass& pass) { auto ctx = state.context; auto mesh = (D3D11_Mesh*)pass.mesh.get(); @@ -1038,7 +1038,7 @@ namespace Blah } } - void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) + void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) { if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) { diff --git a/src/internal/graphics_backend_dummy.cpp b/src/internal/graphics_dummy.cpp similarity index 79% rename from src/internal/graphics_backend_dummy.cpp rename to src/internal/graphics_dummy.cpp index ee74453..8ce436c 100644 --- a/src/internal/graphics_backend_dummy.cpp +++ b/src/internal/graphics_dummy.cpp @@ -1,7 +1,7 @@ #if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11)) -#include "../internal/graphics_backend.h" -#include "../internal/platform_backend.h" +#include "graphics.h" +#include "platform.h" #include namespace Blah @@ -155,58 +155,58 @@ namespace Blah } }; - bool GraphicsBackend::init() + bool Graphics::init() { Log::info("Dummy Renderer"); return true; } - Renderer GraphicsBackend::renderer() + Renderer Graphics::renderer() { return Renderer::None; } - void GraphicsBackend::shutdown() + void Graphics::shutdown() { } - const RendererFeatures& GraphicsBackend::features() + const RendererFeatures& Graphics::features() { static const RendererFeatures features{ false, true, 4096 }; return features; } - void GraphicsBackend::update() {} - void GraphicsBackend::before_render() {} - void GraphicsBackend::after_render() {} + void Graphics::update() {} + void Graphics::before_render() {} + void Graphics::after_render() {} - TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format) + TextureRef Graphics::create_texture(int width, int height, TextureFormat format) { return TextureRef(new Dummy_Texture(width, height, format, false)); } - TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) + TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) { return TargetRef(new Dummy_Target(width, height, attachments, attachmentCount)); } - ShaderRef GraphicsBackend::create_shader(const ShaderData* data) + ShaderRef Graphics::create_shader(const ShaderData* data) { return ShaderRef(new Dummy_Shader(data)); } - MeshRef GraphicsBackend::create_mesh() + MeshRef Graphics::create_mesh() { return MeshRef(new Dummy_Mesh()); } - void GraphicsBackend::render(const RenderPass& pass) + void Graphics::render(const RenderPass& pass) { } - void GraphicsBackend::clear_backbuffer(Color color) + void Graphics::clear_backbuffer(Color color) { } diff --git a/src/internal/graphics_backend_gl.cpp b/src/internal/graphics_gl.cpp similarity index 97% rename from src/internal/graphics_backend_gl.cpp rename to src/internal/graphics_gl.cpp index 2dd94d6..b9879c6 100644 --- a/src/internal/graphics_backend_gl.cpp +++ b/src/internal/graphics_gl.cpp @@ -1,7 +1,7 @@ #ifdef BLAH_GRAPHICS_OPENGL -#include "../internal/graphics_backend.h" -#include "../internal/platform_backend.h" +#include "graphics.h" +#include "platform.h" #include #include #include @@ -1091,21 +1091,21 @@ namespace Blah } }; - bool GraphicsBackend::init() + bool Graphics::init() { gl = State(); // create gl context - gl.context = PlatformBackend::gl_context_create(); + gl.context = Platform::gl_context_create(); if (gl.context == nullptr) { Log::error("Failed to create OpenGL Context"); return false; } - PlatformBackend::gl_context_make_current(gl.context); + Platform::gl_context_make_current(gl.context); // bind opengl functions - #define GL_FUNC(name, ...) gl.name = (State::name ## Func)(PlatformBackend::gl_get_func("gl" #name)); + #define GL_FUNC(name, ...) gl.name = (State::name ## Func)(Platform::gl_get_func("gl" #name)); GL_FUNCTIONS #undef GL_FUNC @@ -1143,27 +1143,27 @@ namespace Blah return true; } - Renderer GraphicsBackend::renderer() + Renderer Graphics::renderer() { return Renderer::OpenGL; } - void GraphicsBackend::shutdown() + void Graphics::shutdown() { - PlatformBackend::gl_context_destroy(gl.context); + Platform::gl_context_destroy(gl.context); gl.context = nullptr; } - const RendererFeatures& GraphicsBackend::features() + const RendererFeatures& Graphics::features() { return gl.features; } - void GraphicsBackend::update() {} - void GraphicsBackend::before_render() {} - void GraphicsBackend::after_render() {} + void Graphics::update() {} + void Graphics::before_render() {} + void Graphics::after_render() {} - TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format) + TextureRef Graphics::create_texture(int width, int height, TextureFormat format) { auto resource = new OpenGL_Texture(width, height, format); @@ -1176,7 +1176,7 @@ namespace Blah return TextureRef(resource); } - TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) + TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) { auto resource = new OpenGL_Target(width, height, attachments, attachmentCount); @@ -1189,7 +1189,7 @@ namespace Blah return TargetRef(resource); } - ShaderRef GraphicsBackend::create_shader(const ShaderData* data) + ShaderRef Graphics::create_shader(const ShaderData* data) { auto resource = new OpenGL_Shader(data); @@ -1202,7 +1202,7 @@ namespace Blah return ShaderRef(resource); } - MeshRef GraphicsBackend::create_mesh() + MeshRef Graphics::create_mesh() { auto resource = new OpenGL_Mesh(); @@ -1215,7 +1215,7 @@ namespace Blah return MeshRef(resource); } - void GraphicsBackend::render(const RenderPass& pass) + void Graphics::render(const RenderPass& pass) { // Bind the Target Point size; @@ -1476,7 +1476,7 @@ namespace Blah } } - void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) + void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) { int clear = 0; diff --git a/src/internal/input_backend.h b/src/internal/input.h similarity index 89% rename from src/internal/input_backend.h rename to src/internal/input.h index 929cad8..472fbfe 100644 --- a/src/internal/input_backend.h +++ b/src/internal/input.h @@ -3,7 +3,7 @@ namespace Blah { - namespace InputBackend + namespace Input { // Initializes the Input State void init(); diff --git a/src/internal/platform_backend.h b/src/internal/platform.h similarity index 97% rename from src/internal/platform_backend.h rename to src/internal/platform.h index 763f15f..a49d990 100644 --- a/src/internal/platform_backend.h +++ b/src/internal/platform.h @@ -7,12 +7,9 @@ namespace Blah { struct Config; - enum class FileMode; - namespace PlatformBackend + namespace Platform { - typedef void* FileHandle; - // Initialize the System bool init(const Config& config); diff --git a/src/internal/platform_backend_sdl2.cpp b/src/internal/platform_backend_sdl2.cpp deleted file mode 100644 index 7b76e07..0000000 --- a/src/internal/platform_backend_sdl2.cpp +++ /dev/null @@ -1,755 +0,0 @@ -#ifdef BLAH_PLATFORM_SDL2 - -#include "../internal/platform_backend.h" -#include "../internal/input_backend.h" -#include "../internal/graphics_backend.h" -#include -#include -#include -#include -#include - -#include - -#if _WIN32 -#include -// on Windows we're using the C++ API for now -#define WIN32_LEAN_AND_MEAN -#include -#include // for SetProcessDPIAware -#include // for File Reading/Writing -#include // for file explore -namespace fs = std::filesystem; -#else -// on non-Windows we use POSIX standard file system stuff -#include -#include -#include -#include -#endif - -using namespace Blah; - -namespace Blah -{ - SDL_Window* window = nullptr; - SDL_Joystick* joysticks[Blah::Input::max_controllers]; - SDL_GameController* gamepads[Blah::Input::max_controllers]; - char* basePath = nullptr; - char* userPath = nullptr; - bool displayed = false; - - void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message) - { - if (priority <= SDL_LOG_PRIORITY_INFO) - Log::info(message); - else if (priority <= SDL_LOG_PRIORITY_WARN) - Log::warn(message); - else - Log::error(message); - } - - int find_joystick_index(SDL_JoystickID instance_id) - { - for (int i = 0; i < Blah::Input::max_controllers; i++) - if (joysticks[i] != nullptr && SDL_JoystickInstanceID(joysticks[i]) == instance_id) - return i; - return -1; - } - - int find_gamepad_index(SDL_JoystickID instance_id) - { - for (int i = 0; i < Blah::Input::max_controllers; i++) - { - if (gamepads[i] != nullptr) - { - auto joystick = SDL_GameControllerGetJoystick(gamepads[i]); - if (SDL_JoystickInstanceID(joystick) == instance_id) - return i; - } - } - return -1; - } - - // Custom File class - class Blah_SDL2_File : public File - { - private: - SDL_RWops* m_handle; - - public: - Blah_SDL2_File(SDL_RWops* handle) - { - m_handle = handle; - } - - ~Blah_SDL2_File() - { - if (m_handle) - SDL_RWclose(m_handle); - } - - size_t length() override - { - return SDL_RWsize(m_handle); - } - - size_t position() override - { - return SDL_RWtell(m_handle); - } - - size_t seek(size_t position) override - { - return SDL_RWseek(m_handle, position, RW_SEEK_SET); - } - - size_t read(unsigned char* buffer, size_t length) override - { - return SDL_RWread(m_handle, buffer, sizeof(char), length); - } - - size_t write(const unsigned char* buffer, size_t length) override - { - return SDL_RWwrite(m_handle, buffer, sizeof(char), length); - } - }; - - bool PlatformBackend::init(const Config& config) - { - // Required to call this for Windows - // I'm not sure why SDL2 doesn't do this on Windows automatically? - #if _WIN32 - SetProcessDPIAware(); - #endif - - // TODO: - // control this via some kind of config flag - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - SDL_LogSetOutputFunction(sdl_log, nullptr); - - // Get SDL version - SDL_version version; - SDL_GetVersion(&version); - Log::info("SDL v%i.%i.%i", version.major, version.minor, version.patch); - - // initialize SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) - { - Log::error("Failed to initialize SDL2"); - return false; - } - - int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; - - // enable OpenGL - if (App::renderer() == Renderer::OpenGL) - { - flags |= SDL_WINDOW_OPENGL; - - #ifdef __EMSCRIPTEN__ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - #else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - // TODO: - // This should be controlled via the gfx api somehow? - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); - #endif - } - - // create the window - window = SDL_CreateWindow(config.name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config.width, config.height, flags); - if (window == nullptr) - { - Log::error("Failed to create a Window"); - return false; - } - - // Scale Window to monitor for High DPI displays - // Other platforms do this automatically ... Windows we need to explitely do so - #if _WIN32 - { - // find the display index - int display = SDL_GetWindowDisplayIndex(window); - float ddpi, hdpi, vdpi; - if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0) - { - // scale the window up basesd on the display DPI - float hidpiRes = 96; - float dpi = (ddpi / hidpiRes); - if (dpi != 1) - { - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(display, &mode); - SDL_SetWindowPosition(window, (int)(mode.w - config.width * dpi) / 2, (int)(mode.h - config.height * dpi) / 2); - SDL_SetWindowSize(window, (int)(config.width * dpi), (int)(config.height * dpi)); - } - } - } - #endif - - // set window properties - SDL_SetWindowResizable(window, SDL_TRUE); - SDL_SetWindowMinimumSize(window, 256, 256); - - return true; - } - - void PlatformBackend::ready() - { - #ifndef __EMSCRIPTEN__ - // enable V-Sync - // TODO: - // This should be a toggle or controllable in some way - if (App::renderer() == Renderer::OpenGL) - SDL_GL_SetSwapInterval(1); - #endif - } - - void PlatformBackend::shutdown() - { - if (window != nullptr) - SDL_DestroyWindow(window); - window = nullptr; - displayed = false; - - if (basePath != nullptr) - SDL_free(basePath); - - if (userPath != nullptr) - SDL_free(userPath); - - SDL_Quit(); - } - - u64 PlatformBackend::ticks() - { - auto counter = SDL_GetPerformanceCounter(); - auto per_second = (double)SDL_GetPerformanceFrequency(); - return (u64)(counter * (Time::ticks_per_second / per_second)); - } - - // Macro defined by X11 conflicts with MouseButton enum - #undef None - - void PlatformBackend::update(InputState& state) - { - // update the mouse every frame - { - int win_x, win_y, x, y; - - SDL_GetWindowPosition(window, &win_x, &win_y); - SDL_GetGlobalMouseState(&x, &y); - - state.mouse.on_move( - Vec2((float)(x - win_x), (float)(y - win_y)), - Vec2((float)x, (float)y)); - } - - // poll normal events - SDL_Event event; - while (SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - auto& config = App::config(); - if (config.on_exit_request != nullptr) - config.on_exit_request(); - } - // Mouse - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - MouseButton btn = MouseButton::None; - if (event.button.button == SDL_BUTTON_LEFT) - btn = MouseButton::Left; - else if (event.button.button == SDL_BUTTON_RIGHT) - btn = MouseButton::Right; - else if (event.button.button == SDL_BUTTON_MIDDLE) - btn = MouseButton::Middle; - - state.mouse.on_press(btn); - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - MouseButton btn = MouseButton::None; - if (event.button.button == SDL_BUTTON_LEFT) - btn = MouseButton::Left; - else if (event.button.button == SDL_BUTTON_RIGHT) - btn = MouseButton::Right; - else if (event.button.button == SDL_BUTTON_MIDDLE) - btn = MouseButton::Middle; - - state.mouse.on_release(btn); - } - else if (event.type == SDL_MOUSEWHEEL) - { - state.mouse.wheel = Point(event.wheel.x, event.wheel.y); - } - // Keyboard - else if (event.type == SDL_KEYDOWN) - { - if (event.key.repeat == 0) - state.keyboard.on_press((Key)event.key.keysym.scancode); - } - else if (event.type == SDL_KEYUP) - { - if (event.key.repeat == 0) - state.keyboard.on_release((Key)event.key.keysym.scancode); - } - else if (event.type == SDL_TEXTINPUT) - { - state.keyboard.text += event.text.text; - } - // Joystick Controller - else if (event.type == SDL_JOYDEVICEADDED) - { - auto index = event.jdevice.which; - - if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers) - { - auto ptr = joysticks[index] = SDL_JoystickOpen(index); - auto name = SDL_JoystickName(ptr); - auto button_count = SDL_JoystickNumButtons(ptr); - auto axis_count = SDL_JoystickNumAxes(ptr); - auto vendor = SDL_JoystickGetVendor(ptr); - auto product = SDL_JoystickGetProduct(ptr); - auto version = SDL_JoystickGetProductVersion(ptr); - - state.controllers[index].on_connect(name, 0, button_count, axis_count, vendor, product, version); - } - } - else if (event.type == SDL_JOYDEVICEREMOVED) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) - { - state.controllers[index].on_disconnect(); - SDL_JoystickClose(joysticks[index]); - } - } - } - else if (event.type == SDL_JOYBUTTONDOWN) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) - state.controllers[index].on_press((Button)event.jbutton.button); - } - } - else if (event.type == SDL_JOYBUTTONUP) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) - state.controllers[index].on_release((Button)event.jbutton.button); - } - } - else if (event.type == SDL_JOYAXISMOTION) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) - { - float value; - if (event.jaxis.value >= 0) - value = event.jaxis.value / 32767.0f; - else - value = event.jaxis.value / 32768.0f; - state.controllers[index].on_axis((Axis)event.jaxis.axis, value); - } - } - } - // Gamepad Controller - else if (event.type == SDL_CONTROLLERDEVICEADDED) - { - auto index = event.cdevice.which; - if (index >= 0 && index < Input::max_controllers) - { - auto ptr = gamepads[index] = SDL_GameControllerOpen(index); - auto name = SDL_GameControllerName(ptr); - auto vendor = SDL_GameControllerGetVendor(ptr); - auto product = SDL_GameControllerGetProduct(ptr); - auto version = SDL_GameControllerGetProductVersion(ptr); - - state.controllers[index].on_connect(name, 1, 15, 6, vendor, product, version); - } - } - else if (event.type == SDL_CONTROLLERDEVICEREMOVED) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - state.controllers[index].on_disconnect(); - SDL_GameControllerClose(gamepads[index]); - } - } - else if (event.type == SDL_CONTROLLERBUTTONDOWN) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - Button button = Button::None; - if (event.cbutton.button >= 0 && event.cbutton.button < 15) - button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! - - state.controllers[index].on_press(button); - } - } - else if (event.type == SDL_CONTROLLERBUTTONUP) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - Button button = Button::None; - if (event.cbutton.button >= 0 && event.cbutton.button < 15) - button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! - - state.controllers[index].on_release(button); - } - } - else if (event.type == SDL_CONTROLLERAXISMOTION) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - Axis axis = Axis::None; - if (event.caxis.axis >= 0 && event.caxis.axis < 6) - axis = (Axis)event.caxis.axis; // NOTE: These map directly to Engine Axis enum! - - float value; - if (event.caxis.value >= 0) - value = event.caxis.value / 32767.0f; - else - value = event.caxis.value / 32768.0f; - - state.controllers[index].on_axis(axis, value); - } - } - } - } - - void PlatformBackend::sleep(int milliseconds) - { - if (milliseconds >= 0) - SDL_Delay((u32)milliseconds); - } - - void PlatformBackend::present() - { - if (App::renderer() == Renderer::OpenGL) - { - SDL_GL_SwapWindow(window); - } - - // display the window - // this avoids a short black screen on macoS - if (!displayed) - { - SDL_ShowWindow(window); - displayed = true; - } - } - - const char* PlatformBackend::get_title() - { - return nullptr; - } - - void PlatformBackend::set_title(const char* title) - { - SDL_SetWindowTitle(window, title); - } - - void PlatformBackend::get_position(int* x, int* y) - { - SDL_GetWindowPosition(window, x, y); - } - - void PlatformBackend::set_position(int x, int y) - { - SDL_SetWindowPosition(window, x, y); - } - - void PlatformBackend::set_fullscreen(bool enabled) - { - if (enabled) - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); - else - SDL_SetWindowFullscreen(window, 0); - } - - void PlatformBackend::get_size(int* width, int* height) - { - SDL_GetWindowSize(window, width, height); - } - - void PlatformBackend::set_size(int width, int height) - { - SDL_SetWindowSize(window, width, height); - } - - void PlatformBackend::get_draw_size(int* width, int* height) - { - if (App::renderer() == Renderer::OpenGL) - { - SDL_GL_GetDrawableSize(window, width, height); - } - else - { - SDL_GetWindowSize(window, width, height); - } - } - - float PlatformBackend::get_content_scale() - { - // TODO: - // This is incorrect! but for some reason the scale - // is HUGE if I use the Display DPI on macOS :/ - #if __APPLE__ - return 2.0f; - #endif - - #if _WIN32 - float hidpiRes = 96; - #else - float hidpiRes = 72; - #endif - - int index = SDL_GetWindowDisplayIndex(window); - if (index < 0) - Log::error(SDL_GetError()); - - float ddpi, x, y; - if (SDL_GetDisplayDPI(index, &ddpi, &x, &y) != 0) - Log::error(SDL_GetError()); - - return (ddpi / hidpiRes); - } - - // FILE IO - - const char* PlatformBackend::app_path() - { - if (basePath == nullptr) - basePath = SDL_GetBasePath(); - return basePath; - } - - const char* PlatformBackend::user_path() - { - if (userPath == nullptr) - { - auto& config = App::config(); - userPath = SDL_GetPrefPath(nullptr, config.name); - } - - return userPath; - } - - // Windows File System methods - #if _WIN32 - - bool PlatformBackend::file_exists(const char* path) - { - return fs::is_regular_file(path); - } - - bool PlatformBackend::file_delete(const char* path) - { - return fs::remove(path); - } - - bool PlatformBackend::dir_create(const char* path) - { - std::error_code error; - return fs::create_directories(path, error); - } - - bool PlatformBackend::dir_exists(const char* path) - { - return fs::is_directory(path); - } - - bool PlatformBackend::dir_delete(const char* path) - { - BLAH_ASSERT(false, "not implemented"); - return false; - } - - void PlatformBackend::dir_enumerate(Vector& list, const char* path, bool recursive) - { - if (fs::is_directory(path)) - { - if (recursive) - { - for (auto& p : fs::recursive_directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); - } - else - { - for (auto& p : fs::directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); - } - } - } - - void PlatformBackend::dir_explore(const char* path) - { - ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); - } - - // Non-Windows File System Methods - #else - - bool PlatformBackend::file_exists(const char* path) - { - struct stat buffer; - return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode); - } - - bool PlatformBackend::file_delete(const char* path) - { - BLAH_ASSERT(false, "not implemented"); - return false; - } - - bool PlatformBackend::dir_create(const char* path) - { - char tmp[265]; - char* p = NULL; - size_t len; - - snprintf(tmp, sizeof(tmp), "%s", path); - len = strlen(tmp); - if (tmp[len - 1] == '/') - tmp[len - 1] = 0; - for (p = tmp + 1; *p; p++) - if (*p == '/') { - *p = 0; - mkdir(tmp, S_IRWXU); - *p = '/'; - } - return mkdir(tmp, S_IRWXU) == 0; - } - - bool PlatformBackend::dir_exists(const char* path) - { - struct stat buffer; - return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode); - } - - bool PlatformBackend::dir_delete(const char* path) - { - BLAH_ASSERT(false, "not implemented"); - return false; - } - - void PlatformBackend::dir_enumerate(Vector& list, const char* path, bool recursive) - { - DIR* dirp = opendir(path); - if (dirp != NULL) - { - struct dirent* dp; - while ((dp = readdir(dirp)) != NULL) - { - if (dp->d_name[0] == '.') - continue; - - FilePath subpath = FilePath(path); - if (subpath.end()[-1] != '/') subpath = subpath.append("/"); - subpath = subpath.append(dp->d_name); - list.push_back(subpath); - - if (recursive && dp->d_type == DT_DIR) - dir_enumerate(list, subpath + "/", true); - } - closedir(dirp); - } - } - - void PlatformBackend::dir_explore(const char* path) - { - BLAH_ASSERT(false, "'dir_explore' Not Implemented"); - } - - #endif - - FileRef PlatformBackend::file_open(const char* path, FileMode mode) - { - const char* sdl_mode = ""; - - switch (mode) - { - case FileMode::OpenRead: - sdl_mode = "rb"; - break; - case FileMode::Open: - sdl_mode = "r+b"; - break; - case FileMode::CreateWrite: - sdl_mode = "wb"; - break; - case FileMode::Create: - sdl_mode = "w+b"; - break; - } - - auto ptr = SDL_RWFromFile(path, sdl_mode); - if (!ptr) - return FileRef(); - - return FileRef(new Blah_SDL2_File(ptr)); - } - - void* PlatformBackend::gl_get_func(const char* name) - { - return SDL_GL_GetProcAddress(name); - } - - void* PlatformBackend::gl_context_create() - { - void* pointer = SDL_GL_CreateContext(window); - if (pointer == nullptr) - Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError()); - return pointer; - } - - void PlatformBackend::gl_context_make_current(void* context) - { - SDL_GL_MakeCurrent(window, context); - } - - void PlatformBackend::gl_context_destroy(void* context) - { - SDL_GL_DeleteContext(context); - } - - void* PlatformBackend::d3d11_get_hwnd() - { - #if _WIN32 - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - SDL_GetWindowWMInfo(window, &info); - return info.info.win.window; - #else - return nullptr; - #endif - } - -} - -#endif // BLAH_PLATFORM_SDL2 diff --git a/src/internal/platform_backend_win32.cpp b/src/internal/platform_backend_win32.cpp deleted file mode 100644 index f6bd330..0000000 --- a/src/internal/platform_backend_win32.cpp +++ /dev/null @@ -1,858 +0,0 @@ -#ifdef BLAH_PLATFORM_WIN32 - -// Note: -// This backend implementation is unfinished! -// It's missing a few things, namely: -// - Controller Support -// (And error testing) - -#include "../internal/platform_backend.h" -#include "../internal/input_backend.h" -#include "../internal/graphics_backend.h" -#include -#include -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include // for SetProcessDPIAware -#include // for File Reading/Writing -#include // for file explore -#include // for known folder -#include // for ticks method - -using namespace Blah; -namespace fs = std::filesystem; - -namespace Blah -{ - // Primary Window - HWND g_hwnd; - - // Working Directories - FilePath g_working_directory; - FilePath g_user_directory; - - // Timestamp for calculating ticks - std::chrono::system_clock::duration g_start_time; - - // OpenGL Methods - // These are only loaded if built using the OpenGL Backend - HMODULE g_opengl_dll; - void* (WINAPI* g_wglGetProcAddress) (const char* proc); - HGLRC(WINAPI* g_wglCreateContext) (HDC hdc); - BOOL(WINAPI* g_wglDeleteContext) (HGLRC hglrc); - BOOL(WINAPI* g_wglMakeCurrent) (HDC hdc, HGLRC hglrc); - - // fullscreen state - RECT g_windowed_position; - bool g_fullscreen = false; - - // current input state - InputState* g_input_state = nullptr; - - // Converts Windows scancode to Blah key - Key scancode_to_key(WPARAM wParam, LPARAM lParam); - - // Main Windows Procedure callback - LRESULT CALLBACK window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - - // Custom File class - class Blah_Win32_File : public File - { - private: - HANDLE m_handle; - - public: - Blah_Win32_File(HANDLE handle) - { - m_handle = handle; - } - - ~Blah_Win32_File() - { - CloseHandle(m_handle); - } - - size_t length() override - { - // Todo: cache this value? not sure how performant it is - - LARGE_INTEGER file_size; - if (GetFileSizeEx(m_handle, &file_size)) - return file_size.QuadPart; - - return 0; - } - - size_t position() override - { - LARGE_INTEGER move; - LARGE_INTEGER result; - - move.QuadPart = 0; - result.QuadPart = 0; - - SetFilePointerEx(m_handle, move, &result, FILE_CURRENT); - - return result.QuadPart; - } - - size_t seek(size_t position) override - { - LARGE_INTEGER move; - LARGE_INTEGER result; - - move.QuadPart = position; - result.QuadPart = 0; - - SetFilePointerEx(m_handle, move, &result, FILE_BEGIN); - - return result.QuadPart; - } - - size_t read(unsigned char* buffer, size_t length) override - { - static const DWORD read_step = 65536; - - size_t read = 0; - - while (read < length) - { - DWORD to_read = read_step; - if (to_read > length - read) - to_read = (DWORD)(length - read); - - DWORD moved = 0; - if (ReadFile(m_handle, buffer + read, to_read, &moved, NULL)) - read += moved; - - if (moved < to_read) - break; - } - - return read; - } - - size_t write(const unsigned char* buffer, size_t length) override - { - static const DWORD write_step = 65536; - - size_t written = 0; - - while (written < length) - { - DWORD to_write = write_step; - if (to_write > length - written) - to_write = (DWORD)(length - written); - - DWORD moved = 0; - if (WriteFile(m_handle, buffer + written, to_write, &moved, NULL)) - written += moved; - - if (moved < to_write) - break; - } - - return written; - } - }; - - bool PlatformBackend::init(const Config& config) - { - // Required to call this for Windows - SetProcessDPIAware(); - - // Get the hInstance - HINSTANCE hInstance = GetModuleHandle(NULL); - - // Create the Window Class - WNDCLASS wc = {}; - wc.lpfnWndProc = DefWindowProc; - wc.lpszClassName = "BLAH WINDOW"; - wc.hInstance = hInstance; - wc.lpfnWndProc = window_procedure; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hIcon = NULL; - wc.lpszMenuName = NULL; - wc.hbrBackground = (HBRUSH)COLOR_WINDOW; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - - // Register the Window class - RegisterClass(&wc); - - // Create the Window Instance - g_hwnd = CreateWindow("BLAH WINDOW", config.name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL); - - // Failed to create the Window - if (g_hwnd == NULL) - { - Log::error("Window Creation Failed"); - return false; - } - - // Create the OpenGL device info - if (App::renderer() == Renderer::OpenGL) - { - // Load the DLL - g_opengl_dll = LoadLibraryA("opengl32.dll"); - if (g_opengl_dll == NULL) - { - Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); - return false; - } - - // Get the Windows GL functions we need - g_wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_opengl_dll, "wglGetProcAddress"); - g_wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_opengl_dll, "wglCreateContext"); - g_wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_opengl_dll, "wglDeleteContext"); - g_wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_opengl_dll, "wglMakeCurrent"); - - // TODO: - // Allow the user to apply (some of) these values before instantiation. - // Also applies to the SDL2 Backend - - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd - 1, // version number - PFD_DRAW_TO_WINDOW | // support window - PFD_SUPPORT_OPENGL | // support OpenGL - PFD_DOUBLEBUFFER, // double buffered - PFD_TYPE_RGBA, // RGBA type - 32, // 32-bit color depth - 0, 0, 0, 0, 0, 0, // color bits ignored - 0, // no alpha buffer - 0, // shift bit ignored - 0, // no accumulation buffer - 0, 0, 0, 0, // accum bits ignored - 24, // 24-bit z-buffer - 8, // 8-bit stencil buffer - 0, // no auxiliary buffer - PFD_MAIN_PLANE, // main layer - 0, // reserved - 0, 0, 0 // layer masks ignored - }; - - HDC hdc = GetDC(g_hwnd); - - // get the best available match of pixel format for the device context - int pixel_format = ChoosePixelFormat(hdc, &pfd); - - // make that the pixel format of the device context - SetPixelFormat(hdc, pixel_format, &pfd); - } - - // Reset our game timer - g_start_time = std::chrono::system_clock::now().time_since_epoch(); - - // Get Working Directory - { - TCHAR buffer[MAX_PATH]; - GetModuleFileName(NULL, buffer, MAX_PATH); - - auto normalized = Path::normalize(buffer); - auto end = normalized.last_index_of('/');; - if (end >= 0) - g_working_directory = FilePath(normalized.begin(), normalized.begin() + end); - else - g_working_directory = normalized; - g_working_directory.append("/"); - } - - // Get Application User Directory - { - PWSTR path = NULL; - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path))) - { - auto end = path; - while (*end != 0) end++; - - FilePath result; - result.append_utf16((u16*)path, (u16*)end); - - g_user_directory = Path::join(Path::normalize(result), config.name) + "/"; - } - CoTaskMemFree(path); - } - - // Not currently fullscreen - g_fullscreen = false; - - // Finished Platform Setup - return true; - } - - void PlatformBackend::ready() - { - // Setup Window Size - { - auto scale = get_content_scale(); - int sw = (int)(App::config().width * scale); - int sh = (int)(App::config().height * scale); - set_size(sw, sh); - } - - // Display the game window - ShowWindow(g_hwnd, SW_SHOW); - } - - void PlatformBackend::shutdown() - { - DestroyWindow(g_hwnd); - } - - u64 PlatformBackend::ticks() - { - // Todo: - // This should account for whatever Time::ticks_per_second is set to - - auto now = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast(now - g_start_time).count(); - } - - LRESULT CALLBACK window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - { - switch (msg) - { - case WM_CLOSE: - { - auto& config = App::config(); - if (config.on_exit_request != nullptr) - config.on_exit_request(); - return 0; - } - - case WM_DESTROY: - PostQuitMessage(0); - return 0; - - // Mouse Input - case WM_LBUTTONDOWN: - g_input_state->mouse.on_press(MouseButton::Left); - return 0; - - case WM_LBUTTONUP: - g_input_state->mouse.on_release(MouseButton::Left); - return 0; - - case WM_RBUTTONDOWN: - g_input_state->mouse.on_press(MouseButton::Right); - return 0; - - case WM_RBUTTONUP: - g_input_state->mouse.on_release(MouseButton::Right); - return 0; - - case WM_MBUTTONDOWN: - g_input_state->mouse.on_press(MouseButton::Middle); - return 0; - - case WM_MBUTTONUP: - g_input_state->mouse.on_release(MouseButton::Middle); - return 0; - - case WM_MOUSEMOVE: - g_input_state->mouse.on_move(Vec2((float)((u16)lParam), (float)(lParam >> 16)), Vec2::zero); - return 0; - - case WM_MOUSEWHEEL: - g_input_state->mouse.wheel = Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); - return 0; - - // Text Input - case WM_UNICHAR: - if (wParam == UNICODE_NOCHAR) - return 1; - case WM_CHAR: - { - String result; - result.append((u32)wParam); - if (result.length() > 0) - g_input_state->keyboard.text += result.cstr(); - return 0; - } - - // Keyboard Input - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - { - auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1; - if (!is_repeat) - { - auto key = scancode_to_key(wParam, lParam); - if (key != Key::Unknown) - g_input_state->keyboard.on_press(key); - } - return 0; - } - - case WM_KEYUP: - case WM_SYSKEYUP: - { - auto key = scancode_to_key(wParam, lParam); - if (key != Key::Unknown) - g_input_state->keyboard.on_release(key); - return 0; - } - } - - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - void PlatformBackend::update(InputState& state) - { - g_input_state = &state; - - // Catch & Dispatch Window Messages - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - - void PlatformBackend::sleep(int milliseconds) - { - if (milliseconds > 0) - Sleep(milliseconds); - } - - void PlatformBackend::present() - { - if (App::renderer() == Renderer::OpenGL) - { - HDC hdc = GetDC(g_hwnd); - SwapBuffers(hdc); - } - } - - const char* PlatformBackend::get_title() - { - return nullptr; - } - - void PlatformBackend::set_title(const char* title) - { - SetWindowText(g_hwnd, title); - } - - void PlatformBackend::get_position(int* x, int* y) - { - RECT rect; - if (GetWindowRect(g_hwnd, &rect)) - { - *x = rect.left; - *y = rect.top; - } - } - - void PlatformBackend::set_position(int x, int y) - { - int w, h; - get_size(&w, &h); - SetWindowPos(g_hwnd, NULL, x, y, w, h, 0); - } - - void PlatformBackend::set_fullscreen(bool enabled) - { - if (g_fullscreen == enabled) - return; - - g_fullscreen = enabled; - - if (g_fullscreen) - { - GetWindowRect(g_hwnd, &g_windowed_position); - - int w = GetSystemMetrics(SM_CXSCREEN); - int h = GetSystemMetrics(SM_CYSCREEN); - SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); - SetWindowPos(g_hwnd, HWND_TOP, 0, 0, w, h, 0); - ShowWindow(g_hwnd, SW_SHOW); - } - else - { - SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); - SetWindowPos(g_hwnd, HWND_TOP, - g_windowed_position.left, - g_windowed_position.top, - g_windowed_position.right - g_windowed_position.left, - g_windowed_position.bottom - g_windowed_position.top, 0); - ShowWindow(g_hwnd, SW_SHOW); - } - } - - void PlatformBackend::get_size(int* width, int* height) - { - RECT rect; - if (GetClientRect(g_hwnd, &rect)) - { - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; - } - } - - void PlatformBackend::set_size(int width, int height) - { - RECT client_rect; - RECT border_rect; - - GetClientRect(g_hwnd, &client_rect); - GetWindowRect(g_hwnd, &border_rect); - - int border_width = (border_rect.right - border_rect.left) - (client_rect.right - client_rect.left); - int border_height = (border_rect.bottom - border_rect.top) - (client_rect.bottom - client_rect.top); - - SetWindowPos(g_hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0); - } - - void PlatformBackend::get_draw_size(int* width, int* height) - { - RECT rect; - if (GetClientRect(g_hwnd, &rect)) - { - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; - } - } - - float PlatformBackend::get_content_scale() - { - // base value of Windows DPI - // as seen here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow - constexpr float base_raw_value = 96.0f; - - UINT raw_value = GetDpiForWindow(g_hwnd); - - return (raw_value / base_raw_value); - } - - const char* PlatformBackend::app_path() - { - return g_working_directory.cstr(); - } - - const char* PlatformBackend::user_path() - { - return g_user_directory.cstr(); - } - - bool PlatformBackend::file_exists(const char* path) - { - return fs::is_regular_file(path); - } - - bool PlatformBackend::file_delete(const char* path) - { - return fs::remove(path); - } - - bool PlatformBackend::dir_create(const char* path) - { - std::error_code error; - return fs::create_directories(path, error); - } - - bool PlatformBackend::dir_exists(const char* path) - { - return fs::is_directory(path); - } - - bool PlatformBackend::dir_delete(const char* path) - { - BLAH_ASSERT(false, "not implemented"); - return false; - } - - void PlatformBackend::dir_enumerate(Vector& list, const char* path, bool recursive) - { - if (fs::is_directory(path)) - { - if (recursive) - { - for (auto& p : fs::recursive_directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); - } - else - { - for (auto& p : fs::directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); - } - } - } - - void PlatformBackend::dir_explore(const char* path) - { - ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); - } - - FileRef PlatformBackend::file_open(const char* path, FileMode mode) - { - int access = 0; - int creation = 0; - - switch (mode) - { - case FileMode::OpenRead: - access = GENERIC_READ; - creation = OPEN_EXISTING; - break; - case FileMode::Open: - access = GENERIC_READ | GENERIC_WRITE; - creation = OPEN_EXISTING; - break; - case FileMode::CreateWrite: - access = GENERIC_WRITE; - creation = CREATE_ALWAYS; - break; - case FileMode::Create: - access = GENERIC_READ | GENERIC_WRITE; - creation = CREATE_ALWAYS; - break; - } - - auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); - - if (result == INVALID_HANDLE_VALUE) - return FileRef(); - - return FileRef(new Blah_Win32_File(result)); - } - - void* PlatformBackend::gl_get_func(const char* name) - { - // this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions - // wglGetProcAddress doesn't always return valid pointers for some specific methods? - - void* p = (void*)g_wglGetProcAddress(name); - if ((p == 0) || - (p == (void*)0x1) || - (p == (void*)0x2) || - (p == (void*)0x3) || - (p == (void*)-1)) - { - p = (void*)GetProcAddress(g_opengl_dll, name); - } - - return p; - } - - void* PlatformBackend::gl_context_create() - { - HDC hdc = GetDC(g_hwnd); - return g_wglCreateContext(hdc); - } - - void PlatformBackend::gl_context_make_current(void* context) - { - if (context != nullptr) - { - HDC hdc = GetDC(g_hwnd); - g_wglMakeCurrent(hdc, (HGLRC)context); - } - else - g_wglMakeCurrent(NULL, NULL); - } - - void PlatformBackend::gl_context_destroy(void* context) - { - g_wglDeleteContext((HGLRC)context); - } - - void* PlatformBackend::d3d11_get_hwnd() - { - return g_hwnd; - } - - Key scancode_to_key(WPARAM wParam, LPARAM lParam) - { - Key key = Key::Unknown; - - switch (wParam) - { - case VK_CANCEL: key = Key::Cancel; break; - case VK_BACK: key = Key::Backspace; break; - case VK_TAB: key = Key::Tab; break; - case VK_CLEAR: key = Key::Clear; break; - case VK_RETURN: key = Key::Enter; break; - case VK_SHIFT: key = Key::LeftShift; break; - case VK_CONTROL: key = Key::LeftControl; break; - case VK_PAUSE: key = Key::Pause; break; - case VK_CAPITAL: key = Key::Capslock; break; - case VK_ESCAPE: key = Key::Escape; break; - case VK_SPACE: key = Key::Space; break; - case VK_PRIOR: key = Key::Prior; break; - case VK_END: key = Key::End; break; - case VK_HOME: key = Key::Home; break; - case VK_LEFT: key = Key::Left; break; - case VK_UP: key = Key::Up; break; - case VK_RIGHT: key = Key::Right; break; - case VK_DOWN: key = Key::Down; break; - case VK_SELECT: key = Key::Select; break; - case VK_PRINT: key = Key::PrintScreen; break; - case VK_EXECUTE: key = Key::Execute; break; - case VK_SNAPSHOT: key = Key::PrintScreen; break; - case VK_INSERT: key = Key::Insert; break; - case VK_DELETE: key = Key::Delete; break; - case VK_HELP: key = Key::Help; break; - case VK_LWIN: key = Key::LeftOS; break; - case VK_RWIN: key = Key::RightOS; break; - case VK_APPS: key = Key::Application; break; - case VK_SLEEP: key = Key::Unknown; break; - case VK_NUMPAD0: key = Key::Keypad0; break; - case VK_NUMPAD1: key = Key::Keypad1; break; - case VK_NUMPAD2: key = Key::Keypad2; break; - case VK_NUMPAD3: key = Key::Keypad3; break; - case VK_NUMPAD4: key = Key::Keypad4; break; - case VK_NUMPAD5: key = Key::Keypad5; break; - case VK_NUMPAD6: key = Key::Keypad6; break; - case VK_NUMPAD7: key = Key::Keypad7; break; - case VK_NUMPAD8: key = Key::Keypad8; break; - case VK_NUMPAD9: key = Key::Keypad9; break; - case VK_F1: key = Key::F1; break; - case VK_F2: key = Key::F2; break; - case VK_F3: key = Key::F3; break; - case VK_F4: key = Key::F4; break; - case VK_F5: key = Key::F5; break; - case VK_F6: key = Key::F6; break; - case VK_F7: key = Key::F7; break; - case VK_F8: key = Key::F8; break; - case VK_F9: key = Key::F9; break; - case VK_F10: key = Key::F10; break; - case VK_F11: key = Key::F11; break; - case VK_F12: key = Key::F12; break; - case VK_F13: key = Key::F13; break; - case VK_F14: key = Key::F14; break; - case VK_F15: key = Key::F15; break; - case VK_F16: key = Key::F16; break; - case VK_F17: key = Key::F17; break; - case VK_F18: key = Key::F18; break; - case VK_F19: key = Key::F19; break; - case VK_F20: key = Key::F20; break; - case VK_F21: key = Key::F21; break; - case VK_F22: key = Key::F22; break; - case VK_F23: key = Key::F23; break; - case VK_F24: key = Key::F24; break; - case VK_NUMLOCK: key = Key::Numlock; break; - case VK_LSHIFT: key = Key::LeftShift; break; - case VK_RSHIFT: key = Key::RightShift; break; - case VK_LCONTROL: key = Key::LeftControl; break; - case VK_RCONTROL: key = Key::RightControl; break; - case VK_VOLUME_MUTE: key = Key::Mute; break; - case VK_VOLUME_DOWN: key = Key::VolumeDown; break; - case VK_VOLUME_UP: key = Key::VolumeUp; break; - } - - if (key == Key::Unknown) - { - int scancode = (lParam >> 16) & 0xFF; - - switch (scancode) - { - case 1: key = Key::Escape; break; - case 2: key = Key::D1; break; - case 3: key = Key::D2; break; - case 4: key = Key::D3; break; - case 5: key = Key::D4; break; - case 6: key = Key::D5; break; - case 7: key = Key::D6; break; - case 8: key = Key::D7; break; - case 9: key = Key::D8; break; - case 10: key = Key::D9; break; - case 11: key = Key::D0; break; - case 12: key = Key::Minus; break; - case 13: key = Key::Equals; break; - case 14: key = Key::Backspace; break; - case 15: key = Key::Tab; break; - case 16: key = Key::Q; break; - case 17: key = Key::W; break; - case 18: key = Key::E; break; - case 19: key = Key::R; break; - case 20: key = Key::T; break; - case 21: key = Key::Y; break; - case 22: key = Key::U; break; - case 23: key = Key::I; break; - case 24: key = Key::O; break; - case 25: key = Key::P; break; - case 26: key = Key::LeftBracket; break; - case 27: key = Key::RightBracket; break; - case 28: key = Key::Enter; break; - case 29: key = Key::LeftControl; break; - case 30: key = Key::A; break; - case 31: key = Key::S; break; - case 32: key = Key::D; break; - case 33: key = Key::F; break; - case 34: key = Key::G; break; - case 35: key = Key::H; break; - case 36: key = Key::J; break; - case 37: key = Key::K; break; - case 38: key = Key::L; break; - case 39: key = Key::Semicolon; break; - case 40: key = Key::Apostrophe; break; - case 41: key = Key::Tilde; break; - case 42: key = Key::LeftShift; break; - case 43: key = Key::Backslash; break; - case 44: key = Key::Z; break; - case 45: key = Key::X; break; - case 46: key = Key::C; break; - case 47: key = Key::V; break; - case 48: key = Key::B; break; - case 49: key = Key::N; break; - case 50: key = Key::M; break; - case 51: key = Key::Comma; break; - case 52: key = Key::Period; break; - case 53: key = Key::Slash; break; - case 54: key = Key::RightShift; break; - case 55: key = Key::PrintScreen; break; - case 56: key = Key::LeftAlt; break; - case 57: key = Key::Space; break; - case 58: key = Key::Capslock; break; - case 59: key = Key::F1; break; - case 60: key = Key::F2; break; - case 61: key = Key::F3; break; - case 62: key = Key::F4; break; - case 63: key = Key::F5; break; - case 64: key = Key::F6; break; - case 65: key = Key::F7; break; - case 66: key = Key::F8; break; - case 67: key = Key::F9; break; - case 68: key = Key::F10; break; - case 71: key = Key::Home; break; - case 72: key = Key::Up; break; - case 73: key = Key::PageUp; break; - case 74: key = Key::KeypadMinus; break; - case 75: key = Key::Left; break; - case 76: key = Key::Keypad5; break; - case 77: key = Key::Right; break; - case 78: key = Key::KeypadPlus; break; - case 79: key = Key::End; break; - case 80: key = Key::Down; break; - case 81: key = Key::PageDown; break; - case 82: key = Key::Insert; break; - case 83: key = Key::Delete; break; - case 87: key = Key::F11; break; - case 88: key = Key::F12; break; - case 89: key = Key::Pause; break; - case 91: key = Key::LeftOS; break; - case 92: key = Key::RightOS; break; - case 93: key = Key::Application; break; - case 100: key = Key::F13; break; - case 101: key = Key::F14; break; - case 102: key = Key::F15; break; - case 103: key = Key::F16; break; - case 104: key = Key::F17; break; - case 105: key = Key::F18; break; - case 106: key = Key::F19; break; - } - } - - return key; - } -} - -#endif // BLAH_PLATFORM_WIN32 diff --git a/src/internal/platform_sdl2.cpp b/src/internal/platform_sdl2.cpp new file mode 100644 index 0000000..9ef6e7a --- /dev/null +++ b/src/internal/platform_sdl2.cpp @@ -0,0 +1,760 @@ +#ifdef BLAH_PLATFORM_SDL2 + +#include "platform.h" +#include "input.h" +#include "graphics.h" +#include +#include +#include +#include +#include + +#include + +#if _WIN32 +#include +// on Windows we're using the C++ API for now +#define WIN32_LEAN_AND_MEAN +#include +#include // for SetProcessDPIAware +#include // for File Reading/Writing +#include // for file explore +namespace fs = std::filesystem; +#else +// on non-Windows we use POSIX standard file system stuff +#include +#include +#include +#include +#include +#endif + +namespace Blah +{ + // Blah SDL2 Platform State + struct SDL2Platform + { + SDL_Window* window = nullptr; + SDL_Joystick* joysticks[Input::max_controllers]; + SDL_GameController* gamepads[Input::max_controllers]; + char* base_path = nullptr; + char* user_path = nullptr; + bool displayed = false; + }; + + // Blah SDL2 File + class SDL2File : public File + { + private: + SDL_RWops* m_handle; + + public: + SDL2File(SDL_RWops* handle) + { + m_handle = handle; + } + + ~SDL2File() + { + if (m_handle) + SDL_RWclose(m_handle); + } + + size_t length() override + { + return SDL_RWsize(m_handle); + } + + size_t position() override + { + return SDL_RWtell(m_handle); + } + + size_t seek(size_t position) override + { + return SDL_RWseek(m_handle, position, RW_SEEK_SET); + } + + size_t read(unsigned char* buffer, size_t length) override + { + return SDL_RWread(m_handle, buffer, sizeof(char), length); + } + + size_t write(const unsigned char* buffer, size_t length) override + { + return SDL_RWwrite(m_handle, buffer, sizeof(char), length); + } + }; + + void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message) + { + if (priority <= SDL_LOG_PRIORITY_INFO) + Log::info(message); + else if (priority <= SDL_LOG_PRIORITY_WARN) + Log::warn(message); + else + Log::error(message); + } + + int sdl_find_joystick_index(SDL_Joystick** joysticks, SDL_JoystickID instance_id) + { + for (int i = 0; i < Input::max_controllers; i++) + if (joysticks[i] != nullptr && SDL_JoystickInstanceID(joysticks[i]) == instance_id) + return i; + return -1; + } + + int sdl_find_gamepad_index(SDL_GameController** gamepads, SDL_JoystickID instance_id) + { + for (int i = 0; i < Input::max_controllers; i++) + { + if (gamepads[i] != nullptr) + { + auto joystick = SDL_GameControllerGetJoystick(gamepads[i]); + if (SDL_JoystickInstanceID(joystick) == instance_id) + return i; + } + } + return -1; + } +} + +using namespace Blah; + +static Blah::SDL2Platform g_platform; + +bool Platform::init(const Config& config) +{ + g_platform = SDL2Platform(); + + // Required to call this for Windows + // I'm not sure why SDL2 doesn't do this on Windows automatically? +#if _WIN32 + SetProcessDPIAware(); +#endif + + // TODO: + // control this via some kind of config flag + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); + SDL_LogSetOutputFunction(sdl_log, nullptr); + + // Get SDL version + SDL_version version; + SDL_GetVersion(&version); + Log::info("SDL v%i.%i.%i", version.major, version.minor, version.patch); + + // initialize SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) + { + Log::error("Failed to initialize SDL2"); + return false; + } + + int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + + // enable OpenGL + if (App::renderer() == Renderer::OpenGL) + { + flags |= SDL_WINDOW_OPENGL; + +#ifdef __EMSCRIPTEN__ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // TODO: + // This should be controlled via the gfx api somehow? + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); +#endif + } + + // create the window + g_platform.window = SDL_CreateWindow(config.name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config.width, config.height, flags); + if (g_platform.window == nullptr) + { + Log::error("Failed to create a Window"); + return false; + } + + // Scale Window to monitor for High DPI displays + // Other platforms do this automatically ... Windows we need to explitely do so +#if _WIN32 + { + // find the display index + int display = SDL_GetWindowDisplayIndex(g_platform.window); + float ddpi, hdpi, vdpi; + if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0) + { + // scale the window up basesd on the display DPI + float hidpiRes = 96; + float dpi = (ddpi / hidpiRes); + if (dpi != 1) + { + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(display, &mode); + SDL_SetWindowPosition(g_platform.window, (int)(mode.w - config.width * dpi) / 2, (int)(mode.h - config.height * dpi) / 2); + SDL_SetWindowSize(g_platform.window, (int)(config.width * dpi), (int)(config.height * dpi)); + } + } + } +#endif + + // set window properties + SDL_SetWindowResizable(g_platform.window, SDL_TRUE); + SDL_SetWindowMinimumSize(g_platform.window, 256, 256); + + return true; +} + +void Platform::ready() +{ +#ifndef __EMSCRIPTEN__ + // enable V-Sync + // TODO: + // This should be a toggle or controllable in some way + if (App::renderer() == Renderer::OpenGL) + SDL_GL_SetSwapInterval(1); +#endif +} + +void Platform::shutdown() +{ + if (g_platform.window != nullptr) + SDL_DestroyWindow(g_platform.window); + g_platform.window = nullptr; + g_platform.displayed = false; + + if (g_platform.base_path != nullptr) + SDL_free(g_platform.base_path); + + if (g_platform.user_path != nullptr) + SDL_free(g_platform.user_path); + + SDL_Quit(); +} + +u64 Platform::ticks() +{ + auto counter = SDL_GetPerformanceCounter(); + auto per_second = (double)SDL_GetPerformanceFrequency(); + return (u64)(counter * (Time::ticks_per_second / per_second)); +} + +// Macro defined by X11 conflicts with MouseButton enum +#undef None + +void Platform::update(InputState& state) +{ + // update the mouse every frame + { + int win_x, win_y, x, y; + + SDL_GetWindowPosition(g_platform.window, &win_x, &win_y); + SDL_GetGlobalMouseState(&x, &y); + + state.mouse.on_move( + Vec2((float)(x - win_x), (float)(y - win_y)), + Vec2((float)x, (float)y)); + } + + // poll normal events + SDL_Event event; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + auto& config = App::config(); + if (config.on_exit_request != nullptr) + config.on_exit_request(); + } + // Mouse + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + MouseButton btn = MouseButton::None; + if (event.button.button == SDL_BUTTON_LEFT) + btn = MouseButton::Left; + else if (event.button.button == SDL_BUTTON_RIGHT) + btn = MouseButton::Right; + else if (event.button.button == SDL_BUTTON_MIDDLE) + btn = MouseButton::Middle; + + state.mouse.on_press(btn); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + MouseButton btn = MouseButton::None; + if (event.button.button == SDL_BUTTON_LEFT) + btn = MouseButton::Left; + else if (event.button.button == SDL_BUTTON_RIGHT) + btn = MouseButton::Right; + else if (event.button.button == SDL_BUTTON_MIDDLE) + btn = MouseButton::Middle; + + state.mouse.on_release(btn); + } + else if (event.type == SDL_MOUSEWHEEL) + { + state.mouse.wheel = Point(event.wheel.x, event.wheel.y); + } + // Keyboard + else if (event.type == SDL_KEYDOWN) + { + if (event.key.repeat == 0) + state.keyboard.on_press((Key)event.key.keysym.scancode); + } + else if (event.type == SDL_KEYUP) + { + if (event.key.repeat == 0) + state.keyboard.on_release((Key)event.key.keysym.scancode); + } + else if (event.type == SDL_TEXTINPUT) + { + state.keyboard.text += event.text.text; + } + // Joystick Controller + else if (event.type == SDL_JOYDEVICEADDED) + { + auto index = event.jdevice.which; + + if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers) + { + auto ptr = g_platform.joysticks[index] = SDL_JoystickOpen(index); + auto name = SDL_JoystickName(ptr); + auto button_count = SDL_JoystickNumButtons(ptr); + auto axis_count = SDL_JoystickNumAxes(ptr); + auto vendor = SDL_JoystickGetVendor(ptr); + auto product = SDL_JoystickGetProduct(ptr); + auto version = SDL_JoystickGetProductVersion(ptr); + + state.controllers[index].on_connect(name, 0, button_count, axis_count, vendor, product, version); + } + } + else if (event.type == SDL_JOYDEVICEREMOVED) + { + auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + { + state.controllers[index].on_disconnect(); + SDL_JoystickClose(g_platform.joysticks[index]); + } + } + } + else if (event.type == SDL_JOYBUTTONDOWN) + { + auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + state.controllers[index].on_press((Button)event.jbutton.button); + } + } + else if (event.type == SDL_JOYBUTTONUP) + { + auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + state.controllers[index].on_release((Button)event.jbutton.button); + } + } + else if (event.type == SDL_JOYAXISMOTION) + { + auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + { + float value; + if (event.jaxis.value >= 0) + value = event.jaxis.value / 32767.0f; + else + value = event.jaxis.value / 32768.0f; + state.controllers[index].on_axis((Axis)event.jaxis.axis, value); + } + } + } + // Gamepad Controller + else if (event.type == SDL_CONTROLLERDEVICEADDED) + { + auto index = event.cdevice.which; + if (index >= 0 && index < Input::max_controllers) + { + auto ptr = g_platform.gamepads[index] = SDL_GameControllerOpen(index); + auto name = SDL_GameControllerName(ptr); + auto vendor = SDL_GameControllerGetVendor(ptr); + auto product = SDL_GameControllerGetProduct(ptr); + auto version = SDL_GameControllerGetProductVersion(ptr); + + state.controllers[index].on_connect(name, 1, 15, 6, vendor, product, version); + } + } + else if (event.type == SDL_CONTROLLERDEVICEREMOVED) + { + auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + if (index >= 0) + { + state.controllers[index].on_disconnect(); + SDL_GameControllerClose(g_platform.gamepads[index]); + } + } + else if (event.type == SDL_CONTROLLERBUTTONDOWN) + { + auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + if (index >= 0) + { + Button button = Button::None; + if (event.cbutton.button >= 0 && event.cbutton.button < 15) + button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! + + state.controllers[index].on_press(button); + } + } + else if (event.type == SDL_CONTROLLERBUTTONUP) + { + auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + if (index >= 0) + { + Button button = Button::None; + if (event.cbutton.button >= 0 && event.cbutton.button < 15) + button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! + + state.controllers[index].on_release(button); + } + } + else if (event.type == SDL_CONTROLLERAXISMOTION) + { + auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + if (index >= 0) + { + Axis axis = Axis::None; + if (event.caxis.axis >= 0 && event.caxis.axis < 6) + axis = (Axis)event.caxis.axis; // NOTE: These map directly to Engine Axis enum! + + float value; + if (event.caxis.value >= 0) + value = event.caxis.value / 32767.0f; + else + value = event.caxis.value / 32768.0f; + + state.controllers[index].on_axis(axis, value); + } + } + } +} + +void Platform::sleep(int milliseconds) +{ + if (milliseconds >= 0) + SDL_Delay((u32)milliseconds); +} + +void Platform::present() +{ + if (App::renderer() == Renderer::OpenGL) + { + SDL_GL_SwapWindow(g_platform.window); + } + + // display the window + // this avoids a short black screen on macoS + if (!g_platform.displayed) + { + SDL_ShowWindow(g_platform.window); + g_platform.displayed = true; + } +} + +const char* Platform::get_title() +{ + return nullptr; +} + +void Platform::set_title(const char* title) +{ + SDL_SetWindowTitle(g_platform.window, title); +} + +void Platform::get_position(int* x, int* y) +{ + SDL_GetWindowPosition(g_platform.window, x, y); +} + +void Platform::set_position(int x, int y) +{ + SDL_SetWindowPosition(g_platform.window, x, y); +} + +void Platform::set_fullscreen(bool enabled) +{ + if (enabled) + SDL_SetWindowFullscreen(g_platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + else + SDL_SetWindowFullscreen(g_platform.window, 0); +} + +void Platform::get_size(int* width, int* height) +{ + SDL_GetWindowSize(g_platform.window, width, height); +} + +void Platform::set_size(int width, int height) +{ + SDL_SetWindowSize(g_platform.window, width, height); +} + +void Platform::get_draw_size(int* width, int* height) +{ + if (App::renderer() == Renderer::OpenGL) + { + SDL_GL_GetDrawableSize(g_platform.window, width, height); + } + else + { + SDL_GetWindowSize(g_platform.window, width, height); + } +} + +float Platform::get_content_scale() +{ + // TODO: + // This is incorrect! but for some reason the scale + // is HUGE if I use the Display DPI on macOS :/ +#if __APPLE__ + return 2.0f; +#endif + +#if _WIN32 + float hidpiRes = 96; +#else + float hidpiRes = 72; +#endif + + int index = SDL_GetWindowDisplayIndex(g_platform.window); + if (index < 0) + Log::error(SDL_GetError()); + + float ddpi, x, y; + if (SDL_GetDisplayDPI(index, &ddpi, &x, &y) != 0) + Log::error(SDL_GetError()); + + return (ddpi / hidpiRes); +} + +// FILE IO + +const char* Platform::app_path() +{ + if (g_platform.base_path == nullptr) + g_platform.base_path = SDL_GetBasePath(); + return g_platform.base_path; +} + +const char* Platform::user_path() +{ + if (g_platform.user_path == nullptr) + { + auto& config = App::config(); + g_platform.user_path = SDL_GetPrefPath(nullptr, config.name); + } + + return g_platform.user_path; +} + +FileRef Platform::file_open(const char* path, FileMode mode) +{ + const char* sdl_mode = ""; + + switch (mode) + { + case FileMode::OpenRead: + sdl_mode = "rb"; + break; + case FileMode::Open: + sdl_mode = "r+b"; + break; + case FileMode::CreateWrite: + sdl_mode = "wb"; + break; + case FileMode::Create: + sdl_mode = "w+b"; + break; + } + + auto ptr = SDL_RWFromFile(path, sdl_mode); + if (!ptr) + return FileRef(); + + return FileRef(new SDL2File(ptr)); +} + +// Windows File System methods +#if _WIN32 + +bool Platform::file_exists(const char* path) +{ + return fs::is_regular_file(path); +} + +bool Platform::file_delete(const char* path) +{ + return fs::remove(path); +} + +bool Platform::dir_create(const char* path) +{ + return fs::create_directories(path); +} + +bool Platform::dir_exists(const char* path) +{ + return fs::is_directory(path); +} + +bool Platform::dir_delete(const char* path) +{ + return fs::remove_all(path) > 0; +} + +void Platform::dir_enumerate(Vector& list, const char* path, bool recursive) +{ + if (fs::is_directory(path)) + { + if (recursive) + { + for (auto& p : fs::recursive_directory_iterator(path)) + list.emplace_back(p.path().string().c_str()); + } + else + { + for (auto& p : fs::directory_iterator(path)) + list.emplace_back(p.path().string().c_str()); + } + } +} + +void Platform::dir_explore(const char* path) +{ + ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); +} + +// Non-Windows File System Methods +#else + +bool Platform::file_exists(const char* path) +{ + struct stat buffer; + return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode); +} + +bool Platform::file_delete(const char* path) +{ + return unlink(path) == 0; +} + +bool Platform::dir_create(const char* path) +{ + char tmp[265]; + char* p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp), "%s", path); + len = strlen(tmp); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) + if (*p == '/') { + *p = 0; + mkdir(tmp, S_IRWXU); + *p = '/'; + } + return mkdir(tmp, S_IRWXU) == 0; +} + +bool Platform::dir_exists(const char* path) +{ + struct stat buffer; + return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode); +} + +bool Platform::dir_delete(const char* path) +{ + BLAH_ASSERT(false, "not implemented"); + return false; +} + +void Platform::dir_enumerate(Vector& list, const char* path, bool recursive) +{ + DIR* dirp = opendir(path); + if (dirp != NULL) + { + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) + { + if (dp->d_name[0] == '.') + continue; + + FilePath subpath = FilePath(path); + if (subpath.end()[-1] != '/') subpath = subpath.append("/"); + subpath = subpath.append(dp->d_name); + list.push_back(subpath); + + if (recursive && dp->d_type == DT_DIR) + dir_enumerate(list, subpath + "/", true); + } + closedir(dirp); + } +} + +void Platform::dir_explore(const char* path) +{ + BLAH_ASSERT(false, "'dir_explore' Not Implemented"); +} + +#endif + +void* Platform::gl_get_func(const char* name) +{ + return SDL_GL_GetProcAddress(name); +} + +void* Platform::gl_context_create() +{ + void* pointer = SDL_GL_CreateContext(g_platform.window); + if (pointer == nullptr) + Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError()); + return pointer; +} + +void Platform::gl_context_make_current(void* context) +{ + SDL_GL_MakeCurrent(g_platform.window, context); +} + +void Platform::gl_context_destroy(void* context) +{ + SDL_GL_DeleteContext(context); +} + +void* Platform::d3d11_get_hwnd() +{ +#if _WIN32 + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo(g_platform.window, &info); + return info.info.win.window; +#else + return nullptr; +#endif +} + +#endif // BLAH_PLATFORM_SDL2 diff --git a/src/internal/platform_win32.cpp b/src/internal/platform_win32.cpp new file mode 100644 index 0000000..8b9a16f --- /dev/null +++ b/src/internal/platform_win32.cpp @@ -0,0 +1,856 @@ +#ifdef BLAH_PLATFORM_WIN32 + +// Note: +// This backend implementation is unfinished! +// It's missing a few things, namely: +// - Controller Support +// (And error testing) + +#include "platform.h" +#include "input.h" +#include "graphics.h" +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include // for SetProcessDPIAware +#include // for File Reading/Writing +#include // for file explore +#include // for known folder +#include // for ticks method + +namespace Blah +{ + using Duration = std::chrono::system_clock::duration; + + // Win32 Platform State + struct Win32Platform + { + // Main State + HWND hwnd; + FilePath working_directory; + FilePath user_directory; + Duration start_time; + RECT windowed_position; + bool fullscreen = false; + InputState* input_state = nullptr; + + // OpenGL Methods + // These are only loaded if built using the OpenGL Backend + HMODULE opengl_dll; + void* (WINAPI* wglGetProcAddress) (const char* proc); + HGLRC (WINAPI* wglCreateContext) (HDC hdc); + BOOL (WINAPI* wglDeleteContext) (HGLRC hglrc); + BOOL (WINAPI* wglMakeCurrent) (HDC hdc, HGLRC hglrc); + }; + + // Win32 File Class + class Win32File : public File + { + private: + HANDLE m_handle; + + public: + Win32File(HANDLE handle) + { + m_handle = handle; + } + + ~Win32File() + { + CloseHandle(m_handle); + } + + size_t length() override + { + // Todo: cache this value? not sure how performant it is + + LARGE_INTEGER file_size; + if (GetFileSizeEx(m_handle, &file_size)) + return file_size.QuadPart; + + return 0; + } + + size_t position() override + { + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = 0; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_CURRENT); + + return result.QuadPart; + } + + size_t seek(size_t position) override + { + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = position; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_BEGIN); + + return result.QuadPart; + } + + size_t read(unsigned char* buffer, size_t length) override + { + static const DWORD read_step = 65536; + + size_t read = 0; + + while (read < length) + { + DWORD to_read = read_step; + if (to_read > length - read) + to_read = (DWORD)(length - read); + + DWORD moved = 0; + if (ReadFile(m_handle, buffer + read, to_read, &moved, NULL)) + read += moved; + + if (moved < to_read) + break; + } + + return read; + } + + size_t write(const unsigned char* buffer, size_t length) override + { + static const DWORD write_step = 65536; + + size_t written = 0; + + while (written < length) + { + DWORD to_write = write_step; + if (to_write > length - written) + to_write = (DWORD)(length - written); + + DWORD moved = 0; + if (WriteFile(m_handle, buffer + written, to_write, &moved, NULL)) + written += moved; + + if (moved < to_write) + break; + } + + return written; + } + }; + + // Converts Windows scancode to Blah key + Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam); + + // Main Windows Procedure callback + LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +} + +using namespace Blah; +namespace fs = std::filesystem; + +static Blah::Win32Platform g_platform; + +bool Platform::init(const Config& config) +{ + // clear platform + g_platform = Win32Platform(); + + // Required to call this for Windows + SetProcessDPIAware(); + + // Get the hInstance + HINSTANCE hInstance = GetModuleHandle(NULL); + + // Create the Window Class + WNDCLASS wc = {}; + wc.lpfnWndProc = DefWindowProc; + wc.lpszClassName = "BLAH WINDOW"; + wc.hInstance = hInstance; + wc.lpfnWndProc = win32_window_procedure; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = NULL; + wc.lpszMenuName = NULL; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + + // Register the Window class + RegisterClass(&wc); + + // Create the Window Instance + g_platform.hwnd = CreateWindow("BLAH WINDOW", config.name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL); + + // Failed to create the Window + if (g_platform.hwnd == NULL) + { + Log::error("Window Creation Failed"); + return false; + } + + // Create the OpenGL device info + if (App::renderer() == Renderer::OpenGL) + { + // Load the DLL + g_platform.opengl_dll = LoadLibraryA("opengl32.dll"); + if (g_platform.opengl_dll == NULL) + { + Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); + return false; + } + + // Get the Windows GL functions we need + g_platform.wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_platform.opengl_dll, "wglGetProcAddress"); + g_platform.wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_platform.opengl_dll, "wglCreateContext"); + g_platform.wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_platform.opengl_dll, "wglDeleteContext"); + g_platform.wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_platform.opengl_dll, "wglMakeCurrent"); + + // TODO: + // Allow the user to apply (some of) these values before instantiation. + // Also applies to the SDL2 Backend + + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW | // support window + PFD_SUPPORT_OPENGL | // support OpenGL + PFD_DOUBLEBUFFER, // double buffered + PFD_TYPE_RGBA, // RGBA type + 32, // 32-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 24, // 24-bit z-buffer + 8, // 8-bit stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + + HDC hdc = GetDC(g_platform.hwnd); + + // get the best available match of pixel format for the device context + int pixel_format = ChoosePixelFormat(hdc, &pfd); + + // make that the pixel format of the device context + SetPixelFormat(hdc, pixel_format, &pfd); + } + + // Reset our game timer + g_platform.start_time = std::chrono::system_clock::now().time_since_epoch(); + + // Get Working Directory + { + TCHAR buffer[MAX_PATH]; + GetModuleFileName(NULL, buffer, MAX_PATH); + + auto normalized = Path::normalize(buffer); + auto end = normalized.last_index_of('/');; + if (end >= 0) + g_platform.working_directory = FilePath(normalized.begin(), normalized.begin() + end); + else + g_platform.working_directory = normalized; + g_platform.working_directory.append("/"); + } + + // Get Application User Directory + { + PWSTR path = NULL; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path))) + { + auto end = path; + while (*end != 0) end++; + + FilePath result; + result.append_utf16((u16*)path, (u16*)end); + + g_platform.user_directory = Path::join(Path::normalize(result), config.name) + "/"; + } + CoTaskMemFree(path); + } + + // Not currently fullscreen + g_platform.fullscreen = false; + + // Finished Platform Setup + return true; +} + +void Platform::ready() +{ + // Setup Window Size + { + auto scale = get_content_scale(); + int sw = (int)(App::config().width * scale); + int sh = (int)(App::config().height * scale); + set_size(sw, sh); + } + + // Display the game window + ShowWindow(g_platform.hwnd, SW_SHOW); +} + +void Platform::shutdown() +{ + DestroyWindow(g_platform.hwnd); +} + +u64 Platform::ticks() +{ + // Todo: + // This should account for whatever Time::ticks_per_second is set to + + auto now = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(now - g_platform.start_time).count(); +} + +LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_CLOSE: + { + auto& config = App::config(); + if (config.on_exit_request != nullptr) + config.on_exit_request(); + return 0; + } + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + // Mouse Input + case WM_LBUTTONDOWN: + g_platform.input_state->mouse.on_press(MouseButton::Left); + return 0; + + case WM_LBUTTONUP: + g_platform.input_state->mouse.on_release(MouseButton::Left); + return 0; + + case WM_RBUTTONDOWN: + g_platform.input_state->mouse.on_press(MouseButton::Right); + return 0; + + case WM_RBUTTONUP: + g_platform.input_state->mouse.on_release(MouseButton::Right); + return 0; + + case WM_MBUTTONDOWN: + g_platform.input_state->mouse.on_press(MouseButton::Middle); + return 0; + + case WM_MBUTTONUP: + g_platform.input_state->mouse.on_release(MouseButton::Middle); + return 0; + + case WM_MOUSEMOVE: + g_platform.input_state->mouse.on_move(Vec2((float)((u16)lParam), (float)(lParam >> 16)), Vec2::zero); + return 0; + + case WM_MOUSEWHEEL: + g_platform.input_state->mouse.wheel = Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); + return 0; + + // Text Input + case WM_UNICHAR: + if (wParam == UNICODE_NOCHAR) + return 1; + case WM_CHAR: + { + String result; + result.append((u32)wParam); + if (result.length() > 0) + g_platform.input_state->keyboard.text += result.cstr(); + return 0; + } + + // Keyboard Input + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1; + if (!is_repeat) + { + auto key = Blah::win32_scancode_to_key(wParam, lParam); + if (key != Key::Unknown) + g_platform.input_state->keyboard.on_press(key); + } + return 0; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + { + auto key = Blah::win32_scancode_to_key(wParam, lParam); + if (key != Key::Unknown) + g_platform.input_state->keyboard.on_release(key); + return 0; + } + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void Platform::update(InputState& state) +{ + // store reference to input state + g_platform.input_state = &state; + + // Catch & Dispatch Window Messages + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void Platform::sleep(int milliseconds) +{ + if (milliseconds > 0) + Sleep(milliseconds); +} + +void Platform::present() +{ + if (App::renderer() == Renderer::OpenGL) + { + HDC hdc = GetDC(g_platform.hwnd); + SwapBuffers(hdc); + } +} + +const char* Platform::get_title() +{ + return nullptr; +} + +void Platform::set_title(const char* title) +{ + SetWindowText(g_platform.hwnd, title); +} + +void Platform::get_position(int* x, int* y) +{ + RECT rect; + if (GetWindowRect(g_platform.hwnd, &rect)) + { + *x = rect.left; + *y = rect.top; + } +} + +void Platform::set_position(int x, int y) +{ + int w, h; + get_size(&w, &h); + SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0); +} + +void Platform::set_fullscreen(bool enabled) +{ + if (g_platform.fullscreen == enabled) + return; + + g_platform.fullscreen = enabled; + + if (g_platform.fullscreen) + { + GetWindowRect(g_platform.hwnd, &g_platform.windowed_position); + + int w = GetSystemMetrics(SM_CXSCREEN); + int h = GetSystemMetrics(SM_CYSCREEN); + SetWindowLongPtr(g_platform.hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); + SetWindowPos(g_platform.hwnd, HWND_TOP, 0, 0, w, h, 0); + ShowWindow(g_platform.hwnd, SW_SHOW); + } + else + { + SetWindowLongPtr(g_platform.hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); + SetWindowPos(g_platform.hwnd, HWND_TOP, + g_platform.windowed_position.left, + g_platform.windowed_position.top, + g_platform.windowed_position.right - g_platform.windowed_position.left, + g_platform.windowed_position.bottom - g_platform.windowed_position.top, 0); + ShowWindow(g_platform.hwnd, SW_SHOW); + } +} + +void Platform::get_size(int* width, int* height) +{ + RECT rect; + if (GetClientRect(g_platform.hwnd, &rect)) + { + *width = rect.right - rect.left; + *height = rect.bottom - rect.top; + } +} + +void Platform::set_size(int width, int height) +{ + RECT client_rect; + RECT border_rect; + + GetClientRect(g_platform.hwnd, &client_rect); + GetWindowRect(g_platform.hwnd, &border_rect); + + int border_width = (border_rect.right - border_rect.left) - (client_rect.right - client_rect.left); + int border_height = (border_rect.bottom - border_rect.top) - (client_rect.bottom - client_rect.top); + + SetWindowPos(g_platform.hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0); +} + +void Platform::get_draw_size(int* width, int* height) +{ + RECT rect; + if (GetClientRect(g_platform.hwnd, &rect)) + { + *width = rect.right - rect.left; + *height = rect.bottom - rect.top; + } +} + +float Platform::get_content_scale() +{ + // base value of Windows DPI + // as seen here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow + constexpr float base_raw_value = 96.0f; + + UINT raw_value = GetDpiForWindow(g_platform.hwnd); + + return (raw_value / base_raw_value); +} + +const char* Platform::app_path() +{ + return g_platform.working_directory.cstr(); +} + +const char* Platform::user_path() +{ + return g_platform.user_directory.cstr(); +} + +bool Platform::file_exists(const char* path) +{ + return fs::is_regular_file(path); +} + +bool Platform::file_delete(const char* path) +{ + return fs::remove(path); +} + +bool Platform::dir_create(const char* path) +{ + std::error_code error; + return fs::create_directories(path, error); +} + +bool Platform::dir_exists(const char* path) +{ + return fs::is_directory(path); +} + +bool Platform::dir_delete(const char* path) +{ + return fs::remove_all(path) > 0; +} + +void Platform::dir_enumerate(Vector& list, const char* path, bool recursive) +{ + if (fs::is_directory(path)) + { + if (recursive) + { + for (auto& p : fs::recursive_directory_iterator(path)) + list.emplace_back(p.path().string().c_str()); + } + else + { + for (auto& p : fs::directory_iterator(path)) + list.emplace_back(p.path().string().c_str()); + } + } +} + +void Platform::dir_explore(const char* path) +{ + ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); +} + +FileRef Platform::file_open(const char* path, FileMode mode) +{ + int access = 0; + int creation = 0; + + switch (mode) + { + case FileMode::OpenRead: + access = GENERIC_READ; + creation = OPEN_EXISTING; + break; + case FileMode::Open: + access = GENERIC_READ | GENERIC_WRITE; + creation = OPEN_EXISTING; + break; + case FileMode::CreateWrite: + access = GENERIC_WRITE; + creation = CREATE_ALWAYS; + break; + case FileMode::Create: + access = GENERIC_READ | GENERIC_WRITE; + creation = CREATE_ALWAYS; + break; + } + + auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + + if (result == INVALID_HANDLE_VALUE) + return FileRef(); + + return FileRef(new Win32File(result)); +} + +void* Platform::gl_get_func(const char* name) +{ + // this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions + // wglGetProcAddress doesn't always return valid pointers for some specific methods? + + void* p = (void*)g_platform.wglGetProcAddress(name); + if ((p == 0) || + (p == (void*)0x1) || + (p == (void*)0x2) || + (p == (void*)0x3) || + (p == (void*)-1)) + { + p = (void*)GetProcAddress(g_platform.opengl_dll, name); + } + + return p; +} + +void* Platform::gl_context_create() +{ + HDC hdc = GetDC(g_platform.hwnd); + return g_platform.wglCreateContext(hdc); +} + +void Platform::gl_context_make_current(void* context) +{ + if (context != nullptr) + { + HDC hdc = GetDC(g_platform.hwnd); + g_platform.wglMakeCurrent(hdc, (HGLRC)context); + } + else + g_platform.wglMakeCurrent(NULL, NULL); +} + +void Platform::gl_context_destroy(void* context) +{ + g_platform.wglDeleteContext((HGLRC)context); +} + +void* Platform::d3d11_get_hwnd() +{ + return g_platform.hwnd; +} + +Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam) +{ + // scancodes + switch ((lParam >> 16) & 0xFF) + { + case 1: return Key::Escape; + case 2: return Key::D1; + case 3: return Key::D2; + case 4: return Key::D3; + case 5: return Key::D4; + case 6: return Key::D5; + case 7: return Key::D6; + case 8: return Key::D7; + case 9: return Key::D8; + case 10: return Key::D9; + case 11: return Key::D0; + case 12: return Key::Minus; + case 13: return Key::Equals; + case 14: return Key::Backspace; + case 15: return Key::Tab; + case 16: return Key::Q; + case 17: return Key::W; + case 18: return Key::E; + case 19: return Key::R; + case 20: return Key::T; + case 21: return Key::Y; + case 22: return Key::U; + case 23: return Key::I; + case 24: return Key::O; + case 25: return Key::P; + case 26: return Key::LeftBracket; + case 27: return Key::RightBracket; + case 28: return Key::Enter; + case 29: return Key::LeftControl; + case 30: return Key::A; + case 31: return Key::S; + case 32: return Key::D; + case 33: return Key::F; + case 34: return Key::G; + case 35: return Key::H; + case 36: return Key::J; + case 37: return Key::K; + case 38: return Key::L; + case 39: return Key::Semicolon; + case 40: return Key::Apostrophe; + case 41: return Key::Tilde; + case 42: return Key::LeftShift; + case 43: return Key::Backslash; + case 44: return Key::Z; + case 45: return Key::X; + case 46: return Key::C; + case 47: return Key::V; + case 48: return Key::B; + case 49: return Key::N; + case 50: return Key::M; + case 51: return Key::Comma; + case 52: return Key::Period; + case 53: return Key::Slash; + case 54: return Key::RightShift; + case 55: return Key::PrintScreen; + case 56: return Key::LeftAlt; + case 57: return Key::Space; + case 58: return Key::Capslock; + case 59: return Key::F1; + case 60: return Key::F2; + case 61: return Key::F3; + case 62: return Key::F4; + case 63: return Key::F5; + case 64: return Key::F6; + case 65: return Key::F7; + case 66: return Key::F8; + case 67: return Key::F9; + case 68: return Key::F10; + case 71: return Key::Home; + case 72: return Key::Up; + case 73: return Key::PageUp; + case 74: return Key::KeypadMinus; + case 75: return Key::Left; + case 76: return Key::Keypad5; + case 77: return Key::Right; + case 78: return Key::KeypadPlus; + case 79: return Key::End; + case 80: return Key::Down; + case 81: return Key::PageDown; + case 82: return Key::Insert; + case 83: return Key::Delete; + case 87: return Key::F11; + case 88: return Key::F12; + case 89: return Key::Pause; + case 91: return Key::LeftOS; + case 92: return Key::RightOS; + case 93: return Key::Application; + case 100: return Key::F13; + case 101: return Key::F14; + case 102: return Key::F15; + case 103: return Key::F16; + case 104: return Key::F17; + case 105: return Key::F18; + case 106: return Key::F19; + } + + // virtual keys + switch (wParam) + { + case VK_CANCEL: return Key::Cancel; + case VK_BACK: return Key::Backspace; + case VK_TAB: return Key::Tab; + case VK_CLEAR: return Key::Clear; + case VK_RETURN: return Key::Enter; + case VK_SHIFT: return Key::LeftShift; + case VK_CONTROL: return Key::LeftControl; + case VK_PAUSE: return Key::Pause; + case VK_CAPITAL: return Key::Capslock; + case VK_ESCAPE: return Key::Escape; + case VK_SPACE: return Key::Space; + case VK_PRIOR: return Key::Prior; + case VK_END: return Key::End; + case VK_HOME: return Key::Home; + case VK_LEFT: return Key::Left; + case VK_UP: return Key::Up; + case VK_RIGHT: return Key::Right; + case VK_DOWN: return Key::Down; + case VK_SELECT: return Key::Select; + case VK_PRINT: return Key::PrintScreen; + case VK_EXECUTE: return Key::Execute; + case VK_SNAPSHOT: return Key::PrintScreen; + case VK_INSERT: return Key::Insert; + case VK_DELETE: return Key::Delete; + case VK_HELP: return Key::Help; + case VK_LWIN: return Key::LeftOS; + case VK_RWIN: return Key::RightOS; + case VK_APPS: return Key::Application; + case VK_SLEEP: return Key::Unknown; + case VK_NUMPAD0: return Key::Keypad0; + case VK_NUMPAD1: return Key::Keypad1; + case VK_NUMPAD2: return Key::Keypad2; + case VK_NUMPAD3: return Key::Keypad3; + case VK_NUMPAD4: return Key::Keypad4; + case VK_NUMPAD5: return Key::Keypad5; + case VK_NUMPAD6: return Key::Keypad6; + case VK_NUMPAD7: return Key::Keypad7; + case VK_NUMPAD8: return Key::Keypad8; + case VK_NUMPAD9: return Key::Keypad9; + case VK_F1: return Key::F1; + case VK_F2: return Key::F2; + case VK_F3: return Key::F3; + case VK_F4: return Key::F4; + case VK_F5: return Key::F5; + case VK_F6: return Key::F6; + case VK_F7: return Key::F7; + case VK_F8: return Key::F8; + case VK_F9: return Key::F9; + case VK_F10: return Key::F10; + case VK_F11: return Key::F11; + case VK_F12: return Key::F12; + case VK_F13: return Key::F13; + case VK_F14: return Key::F14; + case VK_F15: return Key::F15; + case VK_F16: return Key::F16; + case VK_F17: return Key::F17; + case VK_F18: return Key::F18; + case VK_F19: return Key::F19; + case VK_F20: return Key::F20; + case VK_F21: return Key::F21; + case VK_F22: return Key::F22; + case VK_F23: return Key::F23; + case VK_F24: return Key::F24; + case VK_NUMLOCK: return Key::Numlock; + case VK_LSHIFT: return Key::LeftShift; + case VK_RSHIFT: return Key::RightShift; + case VK_LCONTROL: return Key::LeftControl; + case VK_RCONTROL: return Key::RightControl; + case VK_VOLUME_MUTE: return Key::Mute; + case VK_VOLUME_DOWN: return Key::VolumeDown; + case VK_VOLUME_UP: return Key::VolumeUp; + } + + return Key::Unknown; +} + +#endif // BLAH_PLATFORM_WIN32 diff --git a/src/streams/filestream.cpp b/src/streams/filestream.cpp index d82f9e3..6f75f89 100644 --- a/src/streams/filestream.cpp +++ b/src/streams/filestream.cpp @@ -1,6 +1,6 @@ #include #include -#include "../internal/platform_backend.h" +#include "../internal/platform.h" #include using namespace Blah;