diff --git a/include/blah.h b/include/blah.h index 9c4c3b0..bed0b83 100644 --- a/include/blah.h +++ b/include/blah.h @@ -1,8 +1,8 @@ #pragma once +#include "blah/common.h" #include "blah/app.h" #include "blah/filesystem.h" -#include "blah/common.h" #include "blah/time.h" #include "blah/input.h" @@ -13,7 +13,6 @@ #include "blah/graphics/batch.h" #include "blah/graphics/spritefont.h" #include "blah/graphics/subtexture.h" - #include "blah/graphics/blend.h" #include "blah/graphics/material.h" #include "blah/graphics/mesh.h" diff --git a/include/blah/app.h b/include/blah/app.h index 81835d1..29cbefd 100644 --- a/include/blah/app.h +++ b/include/blah/app.h @@ -19,7 +19,7 @@ namespace Blah }; // Renderer Information - struct RendererFeatures + struct RendererInfo { // The type of Renderer being used RendererType type = RendererType::None; @@ -142,8 +142,8 @@ namespace Blah // Otherwise this function does nothing. void fullscreen(bool enabled); - // Retrieves the Renderer Features - const RendererFeatures& renderer(); + // Retrieves the Renderer Information + const RendererInfo& renderer(); // Gets the BackBuffer const TargetRef& backbuffer(); diff --git a/src/app.cpp b/src/app.cpp index 9ad5694..be0c117 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2,9 +2,9 @@ #include #include #include +#include "internal/internal.h" #include "internal/platform.h" #include "internal/renderer.h" -#include "internal/input.h" #ifdef __EMSCRIPTEN__ #include @@ -15,86 +15,14 @@ using namespace Blah; #define BLAH_ASSERT_RUNNING() BLAH_ASSERT(app_is_running, "The App is not running (call App::run)") +// Interal Platform Pointer +Platform* App::Internal::platform = nullptr; + +// Internal Renderer Pointer +Renderer* App::Internal::renderer = nullptr; + namespace { - Config app_config; - bool app_is_running = false; - bool app_is_exiting = false; - u64 app_time_last; - u64 app_time_accumulator = 0; - - void app_iterate() - { - // update at a fixed timerate - // TODO: allow a non-fixed step update? - { - u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); - u64 time_curr = Platform::ticks(); - u64 time_diff = time_curr - app_time_last; - app_time_last = time_curr; - app_time_accumulator += time_diff; - - // do not let us run too fast - while (app_time_accumulator < time_target) - { - int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000); - Platform::sleep(milliseconds); - - time_curr = Platform::ticks(); - time_diff = time_curr - app_time_last; - app_time_last = time_curr; - app_time_accumulator += time_diff; - } - - // Do not allow us to fall behind too many updates - // (otherwise we'll get spiral of death) - u64 time_maximum = app_config.max_updates * time_target; - if (app_time_accumulator > time_maximum) - app_time_accumulator = time_maximum; - - // do as many updates as we can - while (app_time_accumulator >= time_target) - { - app_time_accumulator -= time_target; - - Time::delta = (1.0f / app_config.target_framerate); - - if (Time::pause_timer > 0) - { - Time::pause_timer -= Time::delta; - if (Time::pause_timer <= -0.0001) - Time::delta = -Time::pause_timer; - else - continue; - } - - Time::previous_ticks = Time::ticks; - Time::ticks += time_target; - Time::previous_seconds = Time::seconds; - Time::seconds += Time::delta; - - Input::update_state(); - Platform::update(Input::state); - Input::update_bindings(); - Renderer::instance->update(); - - if (app_config.on_update != nullptr) - app_config.on_update(); - } - } - - // render - { - Renderer::instance->before_render(); - - if (app_config.on_render != nullptr) - app_config.on_render(); - - Renderer::instance->after_render(); - Platform::present(); - } - } - // A dummy Frame Buffer that represents the Back Buffer // it doesn't actually contain any textures or details. class BackBuffer final : public Target @@ -103,15 +31,24 @@ namespace Attachments empty_textures; Attachments& textures() override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; } const Attachments& textures() const override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; } - int width() const override { int w, h; Platform::get_draw_size(&w, &h); return w; } - int height() const override { int w, h; Platform::get_draw_size(&w, &h); return h; } + int width() const override { + int w, h; App::Internal::platform->get_draw_size(&w, &h); return w; + } + int height() const override { int w, h; App::Internal::platform->get_draw_size(&w, &h); return h; } void clear(Color color, float depth, u8 stencil, ClearMask mask) override { BLAH_ASSERT_RENDERER(); - if (Renderer::instance) Renderer::instance->clear_backbuffer(color, depth, stencil, mask); + if (App::Internal::renderer) + App::Internal::renderer->clear_backbuffer(color, depth, stencil, mask); } }; + // Global App State + Config app_config; + bool app_is_running = false; + bool app_is_exiting = false; + u64 app_time_last; + u64 app_time_accumulator = 0; TargetRef app_backbuffer; } @@ -136,8 +73,12 @@ bool App::run(const Config* c) BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0"); BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1"); BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1"); + if (app_is_running || c == nullptr || c->width <= 0 || c->height <= 0 || c->max_updates <= 0 || c->target_framerate <= 0) + { + App::Internal::shutdown(); return false; + } // default values app_is_running = true; @@ -145,81 +86,176 @@ bool App::run(const Config* c) app_backbuffer = TargetRef(new BackBuffer()); // initialize the system - if (!Platform::init(app_config)) + Internal::platform = Platform::try_make_platform(app_config); + if (!Internal::platform) + { + Log::error("Failed to create Platform module"); + App::Internal::shutdown(); + return false; + } + + if (!Internal::platform->init(app_config)) { Log::error("Failed to initialize Platform module"); + App::Internal::shutdown(); return false; } // initialize graphics + Internal::renderer = Renderer::try_make_renderer(app_config.renderer_type); + if (Internal::renderer == nullptr) { - // instantiate - Renderer::instance = Renderer::try_make_renderer(app_config.renderer_type); + Log::error("Renderer module was not found"); + App::Internal::shutdown(); + return false; + } - // wasn't able to make any - if (Renderer::instance == nullptr) - { - Log::error("Renderer implementation was not found"); - return false; - } - - if (!Renderer::instance->init()) - { - Log::error("Renderer failed to initialize"); - delete Renderer::instance; - return false; - } + if (!Internal::renderer->init()) + { + Log::error("Failed to initialize Renderer module"); + App::Internal::shutdown(); + return false; } // input - Input::init(); + Input::Internal::init(); // prepare by updating input & platform once - Input::update_state(); - Platform::update(Input::state); + Input::Internal::update_state(); + Internal::platform->update(Input::state); // startup if (app_config.on_startup != nullptr) app_config.on_startup(); - app_time_last = Platform::ticks(); + app_time_last = Internal::platform->ticks(); app_time_accumulator = 0; // display window - Platform::ready(); + Internal::platform->ready(); // Begin main loop // Emscripten requires the main loop be separated into its own call #ifdef __EMSCRIPTEN__ - emscripten_set_main_loop(app_iterate, 0, 1); + emscripten_set_main_loop(App::Internal::iterate, 0, 1); #else while (!app_is_exiting) - app_iterate(); + App::Internal::iterate(); #endif // shutdown if (app_config.on_shutdown != nullptr) app_config.on_shutdown(); - Renderer::instance->shutdown(); - Platform::shutdown(); + App::Internal::shutdown(); + return true; +} - delete Renderer::instance; - Renderer::instance = nullptr; +void App::Internal::iterate() +{ + // update at a fixed timerate + // TODO: allow a non-fixed step update? + { + u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); + u64 time_curr = App::Internal::platform->ticks(); + u64 time_diff = time_curr - app_time_last; + app_time_last = time_curr; + app_time_accumulator += time_diff; - // clear static state + // do not let us run too fast + while (app_time_accumulator < time_target) + { + int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000); + App::Internal::platform->sleep(milliseconds); + + time_curr = App::Internal::platform->ticks(); + time_diff = time_curr - app_time_last; + app_time_last = time_curr; + app_time_accumulator += time_diff; + } + + // Do not allow us to fall behind too many updates + // (otherwise we'll get spiral of death) + u64 time_maximum = app_config.max_updates * time_target; + if (app_time_accumulator > time_maximum) + app_time_accumulator = time_maximum; + + // do as many updates as we can + while (app_time_accumulator >= time_target) + { + app_time_accumulator -= time_target; + + Time::delta = (1.0f / app_config.target_framerate); + + if (Time::pause_timer > 0) + { + Time::pause_timer -= Time::delta; + if (Time::pause_timer <= -0.0001) + Time::delta = -Time::pause_timer; + else + continue; + } + + Time::previous_ticks = Time::ticks; + Time::ticks += time_target; + Time::previous_seconds = Time::seconds; + Time::seconds += Time::delta; + + Input::Internal::update_state(); + platform->update(Input::state); + Input::Internal::update_bindings(); + renderer->update(); + + if (app_config.on_update != nullptr) + app_config.on_update(); + } + } + + // render + { + renderer->before_render(); + + if (app_config.on_render != nullptr) + app_config.on_render(); + + renderer->after_render(); + platform->present(); + } +} + +void App::Internal::shutdown() +{ + Input::Internal::shutdown(); + + if (renderer) + renderer->shutdown(); + + if (platform) + platform->shutdown(); + + if (renderer) + delete renderer; + renderer = nullptr; + + if (platform) + delete platform; + platform = nullptr; + + // clear static App state + app_config = Config(); app_is_running = false; app_is_exiting = false; + app_time_last = 0; + app_time_accumulator = 0; app_backbuffer = TargetRef(); + // clear static Time state Time::ticks = 0; Time::seconds = 0; Time::previous_ticks = 0; Time::previous_seconds = 0; Time::delta = 0; - - return true; } void App::exit() @@ -238,59 +274,59 @@ const Config& App::config() const char* App::path() { BLAH_ASSERT_RUNNING(); - return Platform::app_path(); + return Internal::platform->app_path(); } const char* App::user_path() { BLAH_ASSERT_RUNNING(); - return Platform::user_path(); + return Internal::platform->user_path(); } const char* App::get_title() { BLAH_ASSERT_RUNNING(); - return Platform::get_title(); + return Internal::platform->get_title(); } void App::set_title(const char* title) { BLAH_ASSERT_RUNNING(); - Platform::set_title(title); + Internal::platform->set_title(title); } Point App::get_position() { BLAH_ASSERT_RUNNING(); Point result; - Platform::get_position(&result.x, &result.y); + Internal::platform->get_position(&result.x, &result.y); return result; } void App::set_position(Point point) { BLAH_ASSERT_RUNNING(); - Platform::set_position(point.x, point.y); + Internal::platform->set_position(point.x, point.y); } Point App::get_size() { BLAH_ASSERT_RUNNING(); Point result; - Platform::get_size(&result.x, &result.y); + Internal::platform->get_size(&result.x, &result.y); return result; } void App::set_size(Point point) { BLAH_ASSERT_RUNNING(); - Platform::set_size(point.x, point.y); + Internal::platform->set_size(point.x, point.y); } Point App::get_backbuffer_size() { BLAH_ASSERT_RUNNING(); - if (Renderer::instance) + if (Internal::renderer) return Point(app_backbuffer->width(), app_backbuffer->height()); return Point(0, 0); } @@ -298,26 +334,26 @@ Point App::get_backbuffer_size() float App::content_scale() { BLAH_ASSERT_RUNNING(); - return Platform::get_content_scale(); + return Internal::platform->get_content_scale(); } bool App::focused() { BLAH_ASSERT_RUNNING(); - return Platform::get_focused(); + return Internal::platform->get_focused(); } void App::fullscreen(bool enabled) { BLAH_ASSERT_RUNNING(); - Platform::set_fullscreen(enabled); + Internal::platform->set_fullscreen(enabled); } -const RendererFeatures& App::renderer() +const RendererInfo& App::renderer() { BLAH_ASSERT_RUNNING(); BLAH_ASSERT_RENDERER(); - return Renderer::instance->features; + return Internal::renderer->info; } const TargetRef& App::backbuffer() @@ -329,5 +365,5 @@ const TargetRef& App::backbuffer() void System::open_url(const char* url) { BLAH_ASSERT_RUNNING(); - Platform::open_url(url); + App::Internal::platform->open_url(url); } \ No newline at end of file diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 9c4f8d4..0bf23cb 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -1,56 +1,78 @@ #include #include -#include "internal/platform.h" +#include "internal/internal.h" using namespace Blah; FileRef File::open(const FilePath& path, FileMode mode) { - return Platform::file_open(path.cstr(), mode); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->file_open(path.cstr(), mode); + return FileRef(); } bool File::exists(const FilePath& path) { - return Platform::file_exists(path.cstr()); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->file_exists(path.cstr()); + return false; } bool File::destroy(const FilePath& path) { - return Platform::file_delete(path.cstr()); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->file_delete(path.cstr()); + return false; } bool Directory::create(const FilePath& path) { - return Platform::dir_create(path.cstr()); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->dir_create(path.cstr()); + return false; } bool Directory::exists(const FilePath& path) { - return Platform::dir_exists(path.cstr()); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->dir_exists(path.cstr()); + return false; } bool Directory::destroy(const FilePath& path) { - return Platform::dir_delete(path.cstr()); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + return App::Internal::platform->dir_delete(path.cstr()); + return false; } Vector Directory::enumerate(const FilePath& path, bool recursive) { + BLAH_ASSERT_PLATFORM(); + Vector list; - // get files - Platform::dir_enumerate(list, path.cstr(), recursive); - - // normalize path names - for (auto& it : list) - it.replace('\\', '/'); + if (App::Internal::platform) + { + App::Internal::platform->dir_enumerate(list, path.cstr(), recursive); + for (auto& it : list) + it.replace('\\', '/'); + } return list; } void Directory::explore(const FilePath& path) { - Platform::dir_explore(path); + BLAH_ASSERT_PLATFORM(); + if (App::Internal::platform) + App::Internal::platform->dir_explore(path); } FilePath Path::get_file_name(const FilePath& path) diff --git a/src/graphics/batch.cpp b/src/graphics/batch.cpp index 3444122..3f27481 100644 --- a/src/graphics/batch.cpp +++ b/src/graphics/batch.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "../internal/renderer.h" +#include "../internal/internal.h" #include using namespace Blah; @@ -288,7 +288,7 @@ void Batch::render(const TargetRef& target, const Mat4x4f& matrix) if (!m_default_material) { BLAH_ASSERT_RENDERER(); - m_default_material = Material::create(Renderer::instance->default_batcher_shader); + m_default_material = Material::create(App::Internal::renderer->default_batcher_shader); } } diff --git a/src/graphics/mesh.cpp b/src/graphics/mesh.cpp index 3b5864f..dc31693 100644 --- a/src/graphics/mesh.cpp +++ b/src/graphics/mesh.cpp @@ -1,5 +1,5 @@ #include -#include "../internal/renderer.h" +#include "../internal/internal.h" using namespace Blah; @@ -7,8 +7,8 @@ MeshRef Mesh::create() { BLAH_ASSERT_RENDERER(); - if (Renderer::instance) - return Renderer::instance->create_mesh(); + if (App::Internal::renderer) + return App::Internal::renderer->create_mesh(); return MeshRef(); } diff --git a/src/graphics/renderpass.cpp b/src/graphics/renderpass.cpp index bae713a..97233a9 100644 --- a/src/graphics/renderpass.cpp +++ b/src/graphics/renderpass.cpp @@ -1,6 +1,6 @@ #include #include -#include "../internal/renderer.h" +#include "../internal/internal.h" using namespace Blah; @@ -28,7 +28,7 @@ void RenderPass::perform() BLAH_ASSERT(material->shader(), "Trying to draw with an invalid Shader"); BLAH_ASSERT(mesh, "Trying to draw with an invalid Mesh"); - if (!Renderer::instance) + if (!App::Internal::renderer) return; // copy call @@ -90,5 +90,5 @@ void RenderPass::perform() pass.scissor = pass.scissor.overlap_rect(Rectf(0, 0, draw_size.x, draw_size.y)); // perform render - Renderer::instance->render(pass); + App::Internal::renderer->render(pass); } diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 728a111..5ae6a97 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -1,6 +1,6 @@ #include #include -#include "../internal/renderer.h" +#include "../internal/internal.h" using namespace Blah; @@ -13,8 +13,8 @@ ShaderRef Shader::create(const ShaderData& data) ShaderRef shader; - if (Renderer::instance) - shader = Renderer::instance->create_shader(&data); + if (App::Internal::renderer) + shader = App::Internal::renderer->create_shader(&data); // validate the shader if (shader) diff --git a/src/graphics/target.cpp b/src/graphics/target.cpp index 7d3a1f7..5c7845c 100644 --- a/src/graphics/target.cpp +++ b/src/graphics/target.cpp @@ -1,5 +1,5 @@ #include -#include "../internal/renderer.h" +#include "../internal/internal.h" using namespace Blah; @@ -32,8 +32,8 @@ 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"); - if (Renderer::instance) - return Renderer::instance->create_target(width, height, textures.data(), textures.size()); + if (App::Internal::renderer) + return App::Internal::renderer->create_target(width, height, textures.data(), textures.size()); return TargetRef(); } diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index c988c0a..45fb4b0 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "../internal/renderer.h" +#include "../internal/internal.h" using namespace Blah; @@ -17,9 +17,9 @@ 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"); - if (Renderer::instance) + if (App::Internal::renderer) { - auto tex = Renderer::instance->create_texture(width, height, format); + auto tex = App::Internal::renderer->create_texture(width, height, format); if (tex && data != nullptr) tex->set_data(data); diff --git a/src/input.cpp b/src/input.cpp index 9d8cf18..ade3231 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "internal/input.h" +#include "internal/internal.h" #include "internal/platform.h" #include #include @@ -26,7 +26,7 @@ InputState Blah::Input::last_state; float Blah::Input::repeat_delay = 0.35f; float Blah::Input::repeat_interval = 0.025f; -void Input::init() +void Input::Internal::init() { g_empty_controller.name = "Disconnected"; for (int i = 0; i < Input::max_controllers; i++) @@ -39,7 +39,12 @@ void Input::init() g_sticks.dispose(); } -void Input::update_state() +void Input::Internal::shutdown() +{ + init(); +} + +void Input::Internal::update_state() { // cycle states Input::last_state = Input::state; @@ -75,10 +80,11 @@ void Input::update_state() } // get clipboard - g_clipboard = Platform::get_clipboard(); + if (App::Internal::platform) + g_clipboard = App::Internal::platform->get_clipboard(); } -void Input::update_bindings() +void Input::Internal::update_bindings() { for (int i = 0; i < g_buttons.size(); i++) { @@ -394,7 +400,8 @@ const String& Input::get_clipboard() void Input::set_clipboard(const String& text) { g_clipboard = text; - return Platform::set_clipboard(text); + if (App::Internal::platform) + App::Internal::platform->set_clipboard(text); } ButtonBindingRef Input::register_binding(const ButtonBinding& binding) diff --git a/src/internal/input.h b/src/internal/input.h deleted file mode 100644 index 472fbfe..0000000 --- a/src/internal/input.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -namespace Blah -{ - namespace Input - { - // Initializes the Input State - void init(); - - // Steps the input state - void update_state(); - - // Updates bindings - void update_bindings(); - } -} \ No newline at end of file diff --git a/src/internal/internal.h b/src/internal/internal.h new file mode 100644 index 0000000..0e57966 --- /dev/null +++ b/src/internal/internal.h @@ -0,0 +1,39 @@ +#pragma once +#include "renderer.h" +#include "platform.h" + +#define BLAH_ASSERT_RENDERER() BLAH_ASSERT(App::Internal::renderer, "Renderer has not been created") +#define BLAH_ASSERT_PLATFORM() BLAH_ASSERT(App::Internal::platform, "Platform has not been created") + +namespace Blah +{ + namespace App + { + namespace Internal + { + extern Platform* platform; + extern Renderer* renderer; + + void iterate(); + void shutdown(); + } + } + + namespace Input + { + namespace Internal + { + // Initializes the Input State + void init(); + + // Steps the input state + void update_state(); + + // Updates bindings + void update_bindings(); + + // Clears Input State + void shutdown(); + } + } +} \ No newline at end of file diff --git a/src/internal/platform.h b/src/internal/platform.h index 2e3b2dd..0413575 100644 --- a/src/internal/platform.h +++ b/src/internal/platform.h @@ -8,105 +8,110 @@ namespace Blah { struct Config; - namespace Platform + class Platform { - // Initialize the System - bool init(const Config& config); + public: + + // Initialize the Graphics + virtual bool init(const Config& config) = 0; // Called after the on_startup callback, but before the update loop begins - void ready(); + virtual void ready() = 0; // Called during shutdown - void shutdown(); + virtual void shutdown() = 0; // The time, in ticks (microseconds) since the Application was started - u64 ticks(); + virtual u64 ticks() = 0; // Called every frame - void update(InputState& state); + virtual void update(InputState& state) = 0; // Sleeps the current thread - void sleep(int milliseconds); + virtual void sleep(int milliseconds) = 0; // Called to present the window contents - void present(); + virtual void present() = 0; // Gets the Application Window Title in UTF-8 - const char* get_title(); + virtual const char* get_title() = 0; // Sets the Application Window Title in UTF-8 - void set_title(const char* title); + virtual void set_title(const char* title) = 0; // Gets the Application Window Position, in Screen Coordinates - void get_position(int* x, int* y); + virtual void get_position(int* x, int* y) = 0; // Sets the Application Window Position, in Screen Coordinates - void set_position(int x, int y); + virtual void set_position(int x, int y) = 0; // Gets whether the Window has focus - bool get_focused(); + virtual bool get_focused() = 0; // Sets the Window Fullscreen if enabled is not 0 - void set_fullscreen(bool enabled); + virtual void set_fullscreen(bool enabled) = 0; // Gets the Application Window Size, in Screen Coordinates - void get_size(int* width, int* height); + virtual void get_size(int* width, int* height) = 0; // Sets the Application Window Size, in Screen Coordinates - void set_size(int width, int height); + virtual void set_size(int width, int height) = 0; // Gets the Application Window Drawing Size, in Pixels. This may differ from the Window Size on hi-dpi displays. - void get_draw_size(int* width, int* height); + virtual void get_draw_size(int* width, int* height) = 0; // Gets the Desktop Content Scale. Gui should be scaled by this value - float get_content_scale(); + virtual float get_content_scale() = 0; // Returns the absoluate path to the directory that the application was started from - const char* app_path(); + virtual const char* app_path() = 0; // Returns the absolute path to the user directory where save data and settings should be stored - const char* user_path(); + virtual const char* user_path() = 0; // Opens a file and sets the handle, or returns an empty handle if it fails - FileRef file_open(const char* path, FileMode mode); + virtual FileRef file_open(const char* path, FileMode mode) = 0; // Returns true if a file with the given path exists - bool file_exists(const char* path); + virtual bool file_exists(const char* path) = 0; // Returns true if a file with the given path was deleted - bool file_delete(const char* path); + virtual bool file_delete(const char* path) = 0; // Returns true if a directory with the given path was successfully created - bool dir_create(const char* path); + virtual bool dir_create(const char* path) = 0; // Returns true if a directory with the given path exists - bool dir_exists(const char* path); + virtual bool dir_exists(const char* path) = 0; // Returns true if a directory with the given path was deleted - bool dir_delete(const char* path); + virtual bool dir_delete(const char* path) = 0; // enumerates a directory and appends each file to the given list - void dir_enumerate(Vector& list, const char* path, bool recursive); + virtual void dir_enumerate(Vector& list, const char* path, bool recursive) = 0; // opens a directory in the OS file explorer / finder - void dir_explore(const char* path); + virtual void dir_explore(const char* path) = 0; // sets the contents of the clipboard - void set_clipboard(const char* text); + virtual void set_clipboard(const char* text) = 0; // gets the contents of the clipboard into the given string - const char* get_clipboard(); + virtual const char* get_clipboard() = 0; // Tries to open a URL in a web browser - void open_url(const char* url); + virtual void open_url(const char* url) = 0; // OpenGL Methods - void* gl_get_func(const char* name); - void* gl_context_create(); - void gl_context_make_current(void* context); - void gl_context_destroy(void* context); + virtual void* gl_get_func(const char* name) = 0; + virtual void* gl_context_create() = 0; + virtual void gl_context_make_current(void* context) = 0; + virtual void gl_context_destroy(void* context) = 0; // D3D11 Methods - void* d3d11_get_hwnd(); - } + virtual void* d3d11_get_hwnd() = 0; + + // Instantiates the Platform object + static Platform* try_make_platform(const Config& config); + }; } \ No newline at end of file diff --git a/src/internal/platform_sdl2.cpp b/src/internal/platform_sdl2.cpp index e18d1c3..9f65ad6 100644 --- a/src/internal/platform_sdl2.cpp +++ b/src/internal/platform_sdl2.cpp @@ -1,7 +1,6 @@ #ifdef BLAH_PLATFORM_SDL2 #include "platform.h" -#include "input.h" #include "renderer.h" #include #include @@ -28,30 +27,6 @@ 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; - } g_platform; - - // Blah SDL2 File - struct SDL2File : public File - { - SDL_RWops* handle; - SDL2File(SDL_RWops* handle) : handle(handle) { } - ~SDL2File() { if (handle) SDL_RWclose(handle); } - size_t length() override { return SDL_RWsize(handle); } - size_t position() override { return SDL_RWtell(handle); } - size_t seek(size_t position) override { return SDL_RWseek(handle, position, RW_SEEK_SET); } - size_t read(unsigned char* buffer, size_t length) override { return SDL_RWread(handle, buffer, sizeof(char), length); } - size_t write(const unsigned char* buffer, size_t length) override { return SDL_RWwrite(handle, buffer, sizeof(char), length); } - }; - void blah_sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message) { if (priority <= SDL_LOG_PRIORITY_INFO) @@ -83,14 +58,80 @@ namespace Blah } return -1; } + + struct SDL2_File : public File + { + SDL_RWops* handle; + SDL2_File(SDL_RWops* handle) : handle(handle) { } + ~SDL2_File() { if (handle) SDL_RWclose(handle); } + size_t length() override { return SDL_RWsize(handle); } + size_t position() override { return SDL_RWtell(handle); } + size_t seek(size_t position) override { return SDL_RWseek(handle, position, RW_SEEK_SET); } + size_t read(unsigned char* buffer, size_t length) override { return SDL_RWread(handle, buffer, sizeof(char), length); } + size_t write(const unsigned char* buffer, size_t length) override { return SDL_RWwrite(handle, buffer, sizeof(char), length); } + }; + + struct SDL2_Platform : public Platform + { + SDL_Window* window = nullptr; + SDL_Joystick* joysticks[Input::max_controllers]; + SDL_GameController* gamepads[Input::max_controllers]; + char* base_path_value = nullptr; + char* user_path_value = nullptr; + bool displayed = false; + + SDL2_Platform(); + bool init(const Config& config) override; + void ready() override; + void shutdown() override; + u64 ticks() override; + void update(InputState& state) override; + void sleep(int milliseconds) override; + void present() override; + const char* get_title() override; + void set_title(const char* title) override; + void get_position(int* x, int* y) override; + void set_position(int x, int y) override; + bool get_focused() override; + void set_fullscreen(bool enabled) override; + void get_size(int* width, int* height) override; + void set_size(int width, int height) override; + void get_draw_size(int* width, int* height) override; + float get_content_scale() override; + const char* app_path() override; + const char* user_path() override; + FileRef file_open(const char* path, FileMode mode) override; + bool file_exists(const char* path) override; + bool file_delete(const char* path) override; + bool dir_create(const char* path) override; + bool dir_exists(const char* path) override; + bool dir_delete(const char* path) override; + void dir_enumerate(Vector& list, const char* path, bool recursive) override; + void dir_explore(const char* path) override; + void set_clipboard(const char* text) override; + const char* get_clipboard() override; + void open_url(const char* url) override; + void* gl_get_func(const char* name) override; + void* gl_context_create() override; + void gl_context_make_current(void* context) override; + void gl_context_destroy(void* context) override; + void* d3d11_get_hwnd() override; + }; } using namespace Blah; -bool Platform::init(const Config& config) +SDL2_Platform::SDL2_Platform() { - g_platform = SDL2Platform(); + for (int i = 0; i < Input::max_controllers; i++) + { + joysticks[i] = nullptr; + gamepads[i] = nullptr; + } +} +bool SDL2_Platform::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 @@ -141,8 +182,8 @@ bool Platform::init(const Config& config) } // 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) + 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; @@ -153,7 +194,7 @@ bool Platform::init(const Config& config) #if _WIN32 { // find the display index - int display = SDL_GetWindowDisplayIndex(g_platform.window); + int display = SDL_GetWindowDisplayIndex(window); float ddpi, hdpi, vdpi; if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0) { @@ -164,21 +205,21 @@ bool Platform::init(const Config& config) { 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)); + 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(g_platform.window, SDL_TRUE); - SDL_SetWindowMinimumSize(g_platform.window, 256, 256); + SDL_SetWindowResizable(window, SDL_TRUE); + SDL_SetWindowMinimumSize(window, 256, 256); return true; } -void Platform::ready() +void SDL2_Platform::ready() { #ifndef __EMSCRIPTEN__ // enable V-Sync @@ -189,36 +230,36 @@ void Platform::ready() #endif } -void Platform::shutdown() +void SDL2_Platform::shutdown() { - if (g_platform.window != nullptr) - SDL_DestroyWindow(g_platform.window); - g_platform.window = nullptr; - g_platform.displayed = false; + if (window != nullptr) + SDL_DestroyWindow(window); + window = nullptr; + displayed = false; - if (g_platform.base_path != nullptr) - SDL_free(g_platform.base_path); + if (base_path_value != nullptr) + SDL_free(base_path_value); - if (g_platform.user_path != nullptr) - SDL_free(g_platform.user_path); + if (user_path_value != nullptr) + SDL_free(user_path_value); SDL_Quit(); } -u64 Platform::ticks() +u64 SDL2_Platform::ticks() { auto counter = SDL_GetPerformanceCounter(); auto per_second = (double)SDL_GetPerformanceFrequency(); return (u64)(counter * (Time::ticks_per_second / per_second)); } -void Platform::update(InputState& state) +void SDL2_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_GetWindowPosition(window, &win_x, &win_y); SDL_GetGlobalMouseState(&x, &y); state.mouse.on_move( @@ -287,7 +328,7 @@ void Platform::update(InputState& state) if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers) { - auto ptr = g_platform.joysticks[index] = SDL_JoystickOpen(index); + auto ptr = joysticks[index] = SDL_JoystickOpen(index); auto name = SDL_JoystickName(ptr); auto button_count = SDL_JoystickNumButtons(ptr); auto axis_count = SDL_JoystickNumAxes(ptr); @@ -300,19 +341,19 @@ void Platform::update(InputState& state) } else if (event.type == SDL_JOYDEVICEREMOVED) { - auto index = blah_sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + auto index = blah_sdl_find_joystick_index(joysticks, event.jdevice.which); if (index >= 0) { if (SDL_IsGameController(index) == SDL_FALSE) { state.controllers[index].on_disconnect(); - SDL_JoystickClose(g_platform.joysticks[index]); + SDL_JoystickClose(joysticks[index]); } } } else if (event.type == SDL_JOYBUTTONDOWN) { - auto index = blah_sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + auto index = blah_sdl_find_joystick_index(joysticks, event.jdevice.which); if (index >= 0) { if (SDL_IsGameController(index) == SDL_FALSE) @@ -321,7 +362,7 @@ void Platform::update(InputState& state) } else if (event.type == SDL_JOYBUTTONUP) { - auto index = blah_sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + auto index = blah_sdl_find_joystick_index(joysticks, event.jdevice.which); if (index >= 0) { if (SDL_IsGameController(index) == SDL_FALSE) @@ -330,7 +371,7 @@ void Platform::update(InputState& state) } else if (event.type == SDL_JOYAXISMOTION) { - auto index = blah_sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which); + auto index = blah_sdl_find_joystick_index(joysticks, event.jdevice.which); if (index >= 0) { if (SDL_IsGameController(index) == SDL_FALSE) @@ -350,7 +391,7 @@ void Platform::update(InputState& state) auto index = event.cdevice.which; if (index >= 0 && index < Input::max_controllers) { - auto ptr = g_platform.gamepads[index] = SDL_GameControllerOpen(index); + auto ptr = gamepads[index] = SDL_GameControllerOpen(index); auto name = SDL_GameControllerName(ptr); auto vendor = SDL_GameControllerGetVendor(ptr); auto product = SDL_GameControllerGetProduct(ptr); @@ -361,16 +402,16 @@ void Platform::update(InputState& state) } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { - auto index = blah_sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + auto index = blah_sdl_find_gamepad_index(gamepads, event.cdevice.which); if (index >= 0) { state.controllers[index].on_disconnect(); - SDL_GameControllerClose(g_platform.gamepads[index]); + SDL_GameControllerClose(gamepads[index]); } } else if (event.type == SDL_CONTROLLERBUTTONDOWN) { - auto index = blah_sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + auto index = blah_sdl_find_gamepad_index(gamepads, event.cdevice.which); if (index >= 0) { Button button = Button::None; @@ -382,7 +423,7 @@ void Platform::update(InputState& state) } else if (event.type == SDL_CONTROLLERBUTTONUP) { - auto index = blah_sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + auto index = blah_sdl_find_gamepad_index(gamepads, event.cdevice.which); if (index >= 0) { Button button = Button::None; @@ -394,7 +435,7 @@ void Platform::update(InputState& state) } else if (event.type == SDL_CONTROLLERAXISMOTION) { - auto index = blah_sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which); + auto index = blah_sdl_find_gamepad_index(gamepads, event.cdevice.which); if (index >= 0) { Axis axis = Axis::None; @@ -413,85 +454,85 @@ void Platform::update(InputState& state) } } -void Platform::sleep(int milliseconds) +void SDL2_Platform::sleep(int milliseconds) { if (milliseconds >= 0) SDL_Delay((u32)milliseconds); } -void Platform::present() +void SDL2_Platform::present() { if (App::renderer().type == RendererType::OpenGL) { - SDL_GL_SwapWindow(g_platform.window); + SDL_GL_SwapWindow(window); } // display the window // this avoids a short black screen on macoS - if (!g_platform.displayed) + if (!displayed) { - SDL_ShowWindow(g_platform.window); - g_platform.displayed = true; + SDL_ShowWindow(window); + displayed = true; } } -const char* Platform::get_title() +const char* SDL2_Platform::get_title() { - return SDL_GetWindowTitle(g_platform.window); + return SDL_GetWindowTitle(window); } -void Platform::set_title(const char* title) +void SDL2_Platform::set_title(const char* title) { - SDL_SetWindowTitle(g_platform.window, title); + SDL_SetWindowTitle(window, title); } -void Platform::get_position(int* x, int* y) +void SDL2_Platform::get_position(int* x, int* y) { - SDL_GetWindowPosition(g_platform.window, x, y); + SDL_GetWindowPosition(window, x, y); } -void Platform::set_position(int x, int y) +void SDL2_Platform::set_position(int x, int y) { - SDL_SetWindowPosition(g_platform.window, x, y); + SDL_SetWindowPosition(window, x, y); } -bool Platform::get_focused() +bool SDL2_Platform::get_focused() { - auto flags = SDL_GetWindowFlags(g_platform.window); + auto flags = SDL_GetWindowFlags(window); return (flags & SDL_WINDOW_INPUT_FOCUS) != 0 && (flags & SDL_WINDOW_MINIMIZED) == 0; } -void Platform::set_fullscreen(bool enabled) +void SDL2_Platform::set_fullscreen(bool enabled) { if (enabled) - SDL_SetWindowFullscreen(g_platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); else - SDL_SetWindowFullscreen(g_platform.window, 0); + SDL_SetWindowFullscreen(window, 0); } -void Platform::get_size(int* width, int* height) +void SDL2_Platform::get_size(int* width, int* height) { - SDL_GetWindowSize(g_platform.window, width, height); + SDL_GetWindowSize(window, width, height); } -void Platform::set_size(int width, int height) +void SDL2_Platform::set_size(int width, int height) { - SDL_SetWindowSize(g_platform.window, width, height); + SDL_SetWindowSize(window, width, height); } -void Platform::get_draw_size(int* width, int* height) +void SDL2_Platform::get_draw_size(int* width, int* height) { if (App::renderer().type == RendererType::OpenGL) { - SDL_GL_GetDrawableSize(g_platform.window, width, height); + SDL_GL_GetDrawableSize(window, width, height); } else { - SDL_GetWindowSize(g_platform.window, width, height); + SDL_GetWindowSize(window, width, height); } } -float Platform::get_content_scale() +float SDL2_Platform::get_content_scale() { // TODO: // This is incorrect! but for some reason the scale @@ -506,7 +547,7 @@ float Platform::get_content_scale() float hidpiRes = 72; #endif - int index = SDL_GetWindowDisplayIndex(g_platform.window); + int index = SDL_GetWindowDisplayIndex(window); if (index < 0) Log::error(SDL_GetError()); @@ -517,27 +558,25 @@ float Platform::get_content_scale() return (ddpi / hidpiRes); } -// FILE IO - -const char* Platform::app_path() +const char* SDL2_Platform::app_path() { - if (g_platform.base_path == nullptr) - g_platform.base_path = SDL_GetBasePath(); - return g_platform.base_path; + if (base_path_value == nullptr) + base_path_value = SDL_GetBasePath(); + return base_path_value; } -const char* Platform::user_path() +const char* SDL2_Platform::user_path() { - if (g_platform.user_path == nullptr) + if (user_path_value == nullptr) { auto& config = App::config(); - g_platform.user_path = SDL_GetPrefPath(nullptr, config.name); + user_path_value = SDL_GetPrefPath(nullptr, config.name); } - return g_platform.user_path; + return user_path_value; } -FileRef Platform::file_open(const char* path, FileMode mode) +FileRef SDL2_Platform::file_open(const char* path, FileMode mode) { const char* sdl_mode = ""; @@ -561,35 +600,35 @@ FileRef Platform::file_open(const char* path, FileMode mode) if (!ptr) return FileRef(); - return FileRef(new SDL2File(ptr)); + return FileRef(new SDL2_File(ptr)); } -bool Platform::file_exists(const char* path) +bool SDL2_Platform::file_exists(const char* path) { return std::filesystem::is_regular_file(path); } -bool Platform::file_delete(const char* path) +bool SDL2_Platform::file_delete(const char* path) { return std::filesystem::remove(path); } -bool Platform::dir_create(const char* path) +bool SDL2_Platform::dir_create(const char* path) { return std::filesystem::create_directories(path); } -bool Platform::dir_exists(const char* path) +bool SDL2_Platform::dir_exists(const char* path) { return std::filesystem::is_directory(path); } -bool Platform::dir_delete(const char* path) +bool SDL2_Platform::dir_delete(const char* path) { return std::filesystem::remove_all(path) > 0; } -void Platform::dir_enumerate(Vector& list, const char* path, bool recursive) +void SDL2_Platform::dir_enumerate(Vector& list, const char* path, bool recursive) { if (std::filesystem::is_directory(path)) { @@ -606,7 +645,7 @@ void Platform::dir_enumerate(Vector& list, const char* path, bool recu } } -void Platform::dir_explore(const char* path) +void SDL2_Platform::dir_explore(const char* path) { #if _WIN32 @@ -623,54 +662,59 @@ void Platform::dir_explore(const char* path) #endif } -void Platform::set_clipboard(const char* text) +void SDL2_Platform::set_clipboard(const char* text) { SDL_SetClipboardText(text); } -const char* Platform::get_clipboard() +const char* SDL2_Platform::get_clipboard() { return SDL_GetClipboardText(); } -void* Platform::gl_get_func(const char* name) +void* SDL2_Platform::gl_get_func(const char* name) { return SDL_GL_GetProcAddress(name); } -void* Platform::gl_context_create() +void* SDL2_Platform::gl_context_create() { - void* pointer = SDL_GL_CreateContext(g_platform.window); + void* pointer = SDL_GL_CreateContext(window); if (pointer == nullptr) Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError()); return pointer; } -void Platform::gl_context_make_current(void* context) +void SDL2_Platform::gl_context_make_current(void* context) { - SDL_GL_MakeCurrent(g_platform.window, context); + SDL_GL_MakeCurrent(window, context); } -void Platform::gl_context_destroy(void* context) +void SDL2_Platform::gl_context_destroy(void* context) { SDL_GL_DeleteContext(context); } -void* Platform::d3d11_get_hwnd() +void* SDL2_Platform::d3d11_get_hwnd() { #if _WIN32 SDL_SysWMinfo info; SDL_VERSION(&info.version); - SDL_GetWindowWMInfo(g_platform.window, &info); + SDL_GetWindowWMInfo(window, &info); return info.info.win.window; #else return nullptr; #endif } -void Platform::open_url(const char* url) +void SDL2_Platform::open_url(const char* url) { SDL_OpenURL(url); } +Platform* Platform::try_make_platform(const Config& config) +{ + return new SDL2_Platform(); +} + #endif // BLAH_PLATFORM_SDL2 diff --git a/src/internal/platform_win32.cpp b/src/internal/platform_win32.cpp index 2f5e6b9..4089eeb 100644 --- a/src/internal/platform_win32.cpp +++ b/src/internal/platform_win32.cpp @@ -4,8 +4,7 @@ // This is unfinished! It is missing Controller Support! #include "platform.h" -#include "input.h" -#include "renderer.h" +#include "internal.h" #include #include #include @@ -33,8 +32,23 @@ namespace Blah typedef BOOL(WINAPI* wglDeleteContext_fn)(HGLRC); typedef BOOL(WINAPI* wglMakeCurrent_fn)(HDC, HGLRC); - // Win32 Platform State - struct Win32Platform + class Win32File : public File + { + private: + HANDLE m_handle; + LARGE_INTEGER m_size; + + public: + Win32File(HANDLE handle); + ~Win32File(); + size_t length() override; + size_t position() override; + size_t seek(size_t position) override; + size_t read(unsigned char* buffer, size_t length) override; + size_t write(const unsigned char* buffer, size_t length) override; + }; + + struct Win32_Platform : public Platform { // Main State HWND hwnd; @@ -59,7 +73,7 @@ namespace Blah bool connected = false; bool accounted = false; GUID dinstance = GUID_NULL; - DWORD xindex = 0; + DWORD xindex = 0; } joysticks[Input::max_controllers]; // OpenGL Methods @@ -72,106 +86,44 @@ namespace Blah wglDeleteContext_fn delete_context; wglMakeCurrent_fn make_current; } gl; - } g_platform; - // Win32 File Class - class Win32File : public File - { - private: - HANDLE m_handle; - LARGE_INTEGER m_size; + bool init(const Config& config) override; + void ready() override; + void shutdown() override; + u64 ticks() override; + void update(InputState& state) override; + void sleep(int milliseconds) override; + void present() override; + const char* get_title() override; + void set_title(const char* title) override; + void get_position(int* x, int* y) override; + void set_position(int x, int y) override; + bool get_focused() override; + void set_fullscreen(bool enabled) override; + void get_size(int* width, int* height) override; + void set_size(int width, int height) override; + void get_draw_size(int* width, int* height) override; + float get_content_scale() override; + const char* app_path() override; + const char* user_path() override; + FileRef file_open(const char* path, FileMode mode) override; + bool file_exists(const char* path) override; + bool file_delete(const char* path) override; + bool dir_create(const char* path) override; + bool dir_exists(const char* path) override; + bool dir_delete(const char* path) override; + void dir_enumerate(Vector& list, const char* path, bool recursive) override; + void dir_explore(const char* path) override; + void set_clipboard(const char* text) override; + const char* get_clipboard() override; + void open_url(const char* url) override; + void* gl_get_func(const char* name) override; + void* gl_context_create() override; + void gl_context_make_current(void* context) override; + void gl_context_destroy(void* context) override; + void* d3d11_get_hwnd() override; - public: - Win32File(HANDLE handle) - { - m_handle = handle; - - LARGE_INTEGER file_size; - if (GetFileSizeEx(m_handle, &file_size)) - m_size = file_size; - } - - ~Win32File() - { - CloseHandle(m_handle); - } - - size_t length() override - { - return m_size.QuadPart; - } - - 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; - } + void detect_joysticks(); }; // Main Windows Procedure callback @@ -179,19 +131,104 @@ namespace Blah // Converts Windows scancode to Blah key Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam); - - // Detects joysticks (connects & disconnects) - void win32_detect_joysticks(); } using namespace Blah; -namespace fs = std::filesystem; -bool Platform::init(const Config& config) +Win32File::Win32File(HANDLE handle) { - // clear platform - g_platform = Win32Platform(); + m_handle = handle; + m_size.QuadPart = 0; + LARGE_INTEGER file_size; + if (GetFileSizeEx(m_handle, &file_size)) + m_size = file_size; +} + +Win32File::~Win32File() +{ + CloseHandle(m_handle); +} + +size_t Win32File::length() +{ + return m_size.QuadPart; +} + +size_t Win32File::position() +{ + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = 0; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_CURRENT); + + return result.QuadPart; +} + +size_t Win32File::seek(size_t position) +{ + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = position; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_BEGIN); + + return result.QuadPart; +} + +size_t Win32File::read(unsigned char* buffer, size_t length) +{ + 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 Win32File::write(const unsigned char* buffer, size_t length) +{ + 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 Win32_Platform::init(const Config& config) +{ // Required to call this for Windows SetProcessDPIAware(); @@ -215,10 +252,10 @@ bool Platform::init(const Config& config) 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); + 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) + if (hwnd == NULL) { Log::error("Window Creation Failed"); return false; @@ -236,18 +273,18 @@ bool Platform::init(const Config& config) if (config.renderer_type == RendererType::OpenGL) { // Load the DLL - g_platform.gl.dll = LoadLibraryA("opengl32.dll"); - if (g_platform.gl.dll == NULL) + gl.dll = LoadLibraryA("opengl32.dll"); + if (gl.dll == NULL) { Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); return false; } // Get the Windows GL functions we need - g_platform.gl.get_proc_address = (wglGetProcAddress_fn)GetProcAddress(g_platform.gl.dll, "wglGetProcAddress"); - g_platform.gl.create_context = (wglCreateContext_fn)GetProcAddress(g_platform.gl.dll, "wglCreateContext"); - g_platform.gl.delete_context = (wglDeleteContext_fn)GetProcAddress(g_platform.gl.dll, "wglDeleteContext"); - g_platform.gl.make_current = (wglMakeCurrent_fn)GetProcAddress(g_platform.gl.dll, "wglMakeCurrent"); + gl.get_proc_address = (wglGetProcAddress_fn)GetProcAddress(gl.dll, "wglGetProcAddress"); + gl.create_context = (wglCreateContext_fn)GetProcAddress(gl.dll, "wglCreateContext"); + gl.delete_context = (wglDeleteContext_fn)GetProcAddress(gl.dll, "wglDeleteContext"); + gl.make_current = (wglMakeCurrent_fn)GetProcAddress(gl.dll, "wglMakeCurrent"); // TODO: // Allow the user to apply (some of) these values before instantiation. @@ -275,7 +312,7 @@ bool Platform::init(const Config& config) 0, 0, 0 // layer masks ignored }; - HDC hdc = GetDC(g_platform.hwnd); + HDC hdc = GetDC(hwnd); // get the best available match of pixel format for the device context int pixel_format = ChoosePixelFormat(hdc, &pfd); @@ -290,17 +327,17 @@ bool Platform::init(const Config& config) for (int i = 0; dlls[i]; i++) { - g_platform.xinput.dll = LoadLibraryA(dlls[i]); + xinput.dll = LoadLibraryA(dlls[i]); - if (g_platform.xinput.dll) + if (xinput.dll) { - g_platform.xinput.get_capabilities = (XInputGetCapabilities_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetCapabilities"); - g_platform.xinput.get_state = (XInputGetState_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetState"); + xinput.get_capabilities = (XInputGetCapabilities_fn)GetProcAddress(xinput.dll, "XInputGetCapabilities"); + xinput.get_state = (XInputGetState_fn)GetProcAddress(xinput.dll, "XInputGetState"); break; } } - if (!g_platform.xinput.dll) + if (!xinput.dll) Log::warn("Failed to find XInput dll; No Controller Support"); } @@ -312,10 +349,10 @@ bool Platform::init(const Config& config) auto normalized = Path::normalize(buffer); auto end = normalized.last_index_of('/');; if (end >= 0) - g_platform.working_directory = FilePath(normalized.begin(), normalized.begin() + end); + working_directory = FilePath(normalized.begin(), normalized.begin() + end); else - g_platform.working_directory = normalized; - g_platform.working_directory.append("/"); + working_directory = normalized; + working_directory.append("/"); } // Get Application User Directory @@ -329,156 +366,56 @@ bool Platform::init(const Config& config) FilePath result; result.append_utf16((u16*)path, (u16*)end); - g_platform.user_directory = Path::join(Path::normalize(result), config.name) + "/"; + user_directory = Path::join(Path::normalize(result), config.name) + "/"; } CoTaskMemFree(path); } // Reset our game timer - g_platform.start_time = std::chrono::system_clock::now().time_since_epoch(); + start_time = std::chrono::system_clock::now().time_since_epoch(); // Not currently fullscreen - g_platform.fullscreen = false; + fullscreen = false; // Finished Platform Setup return true; } -void Platform::ready() +void Win32_Platform::ready() { // Display the game window - ShowWindow(g_platform.hwnd, SW_SHOW); + ShowWindow(hwnd, SW_SHOW); } -void Platform::shutdown() +void Win32_Platform::shutdown() { - if (g_platform.xinput.dll) - FreeLibrary(g_platform.xinput.dll); + if (xinput.dll) + FreeLibrary(xinput.dll); - if (g_platform.gl.dll) - FreeLibrary(g_platform.gl.dll); + if (gl.dll) + FreeLibrary(gl.dll); - DestroyWindow(g_platform.hwnd); + DestroyWindow(hwnd); } -u64 Platform::ticks() +u64 Win32_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(); + return std::chrono::duration_cast(now - 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; - - - // Controller connected event - case WM_DEVICECHANGE: - { - // DBT_DEVNODES_CHANGED = 0x0007 - // https://docs.microsoft.com/en-us/windows/win32/devio/wm-devicechange - if (wParam == 0x0007) - win32_detect_joysticks(); - 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(Vec2f((float)((u16)lParam), (float)(lParam >> 16)), Vec2f::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) +void Win32_Platform::update(InputState& state) { // store reference to input state - bool first_update = g_platform.input_state == nullptr; - g_platform.input_state = &state; + bool first_update = input_state == nullptr; + input_state = &state; // if this is the first update, poll joysticks that are already connected if (first_update) - win32_detect_joysticks(); + detect_joysticks(); // Catch & Dispatch Window Messages MSG msg; @@ -489,187 +426,187 @@ void Platform::update(InputState& state) } } -void Platform::sleep(int milliseconds) +void Win32_Platform::sleep(int milliseconds) { if (milliseconds > 0) Sleep(milliseconds); } -void Platform::present() +void Win32_Platform::present() { if (App::renderer().type == RendererType::OpenGL) { - HDC hdc = GetDC(g_platform.hwnd); + HDC hdc = GetDC(hwnd); SwapBuffers(hdc); } } -const char* Platform::get_title() +const char* Win32_Platform::get_title() { return nullptr; } -void Platform::set_title(const char* title) +void Win32_Platform::set_title(const char* title) { - SetWindowText(g_platform.hwnd, title); + SetWindowText(hwnd, title); } -void Platform::get_position(int* x, int* y) +void Win32_Platform::get_position(int* x, int* y) { RECT rect; - if (GetWindowRect(g_platform.hwnd, &rect)) + if (GetWindowRect(hwnd, &rect)) { *x = rect.left; *y = rect.top; } } -void Platform::set_position(int x, int y) +void Win32_Platform::set_position(int x, int y) { int w, h; get_size(&w, &h); - SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0); + SetWindowPos(hwnd, NULL, x, y, w, h, 0); } -bool Platform::get_focused() +bool Win32_Platform::get_focused() { Log::warn("App::focused not implemented for Win32 yet"); return true; } -void Platform::set_fullscreen(bool enabled) +void Win32_Platform::set_fullscreen(bool enabled) { - if (g_platform.fullscreen == enabled) + if (fullscreen == enabled) return; - g_platform.fullscreen = enabled; + fullscreen = enabled; - if (g_platform.fullscreen) + if (fullscreen) { - GetWindowRect(g_platform.hwnd, &g_platform.windowed_position); + GetWindowRect(hwnd, &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); + SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); + SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, 0); + ShowWindow(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); + SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); + SetWindowPos(hwnd, HWND_TOP, + windowed_position.left, + windowed_position.top, + windowed_position.right - windowed_position.left, + windowed_position.bottom - windowed_position.top, 0); + ShowWindow(hwnd, SW_SHOW); } } -void Platform::get_size(int* width, int* height) +void Win32_Platform::get_size(int* width, int* height) { RECT rect; - if (GetClientRect(g_platform.hwnd, &rect)) + if (GetClientRect(hwnd, &rect)) { *width = rect.right - rect.left; *height = rect.bottom - rect.top; } } -void Platform::set_size(int width, int height) +void Win32_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); + GetClientRect(hwnd, &client_rect); + GetWindowRect(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); + SetWindowPos(hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0); } -void Platform::get_draw_size(int* width, int* height) +void Win32_Platform::get_draw_size(int* width, int* height) { RECT rect; - if (GetClientRect(g_platform.hwnd, &rect)) + if (GetClientRect(hwnd, &rect)) { *width = rect.right - rect.left; *height = rect.bottom - rect.top; } } -float Platform::get_content_scale() +float Win32_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); + UINT raw_value = GetDpiForWindow(hwnd); return (raw_value / base_raw_value); } -const char* Platform::app_path() +const char* Win32_Platform::app_path() { - return g_platform.working_directory.cstr(); + return working_directory.cstr(); } -const char* Platform::user_path() +const char* Win32_Platform::user_path() { - return g_platform.user_directory.cstr(); + return user_directory.cstr(); } -bool Platform::file_exists(const char* path) +bool Win32_Platform::file_exists(const char* path) { - return fs::is_regular_file(path); + return std::filesystem::is_regular_file(path); } -bool Platform::file_delete(const char* path) +bool Win32_Platform::file_delete(const char* path) { - return fs::remove(path); + return std::filesystem::remove(path); } -bool Platform::dir_create(const char* path) +bool Win32_Platform::dir_create(const char* path) { std::error_code error; - return fs::create_directories(path, error); + return std::filesystem::create_directories(path, error); } -bool Platform::dir_exists(const char* path) +bool Win32_Platform::dir_exists(const char* path) { - return fs::is_directory(path); + return std::filesystem::is_directory(path); } -bool Platform::dir_delete(const char* path) +bool Win32_Platform::dir_delete(const char* path) { - return fs::remove_all(path) > 0; + return std::filesystem::remove_all(path) > 0; } -void Platform::dir_enumerate(Vector& list, const char* path, bool recursive) +void Win32_Platform::dir_enumerate(Vector& list, const char* path, bool recursive) { - if (fs::is_directory(path)) + if (std::filesystem::is_directory(path)) { if (recursive) { - for (auto& p : fs::recursive_directory_iterator(path)) + for (auto& p : std::filesystem::recursive_directory_iterator(path)) list.emplace_back(p.path().string().c_str()); } else { - for (auto& p : fs::directory_iterator(path)) + for (auto& p : std::filesystem::directory_iterator(path)) list.emplace_back(p.path().string().c_str()); } } } -void Platform::dir_explore(const char* path) +void Win32_Platform::dir_explore(const char* path) { ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); } -FileRef Platform::file_open(const char* path, FileMode mode) +FileRef Win32_Platform::file_open(const char* path, FileMode mode) { int access = 0; int creation = 0; @@ -702,52 +639,268 @@ FileRef Platform::file_open(const char* path, FileMode mode) return FileRef(new Win32File(result)); } -void* Platform::gl_get_func(const char* name) +void* Win32_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.gl.get_proc_address(name); + void* p = (void*)gl.get_proc_address(name); if ((p == 0) || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1)) { - p = (void*)GetProcAddress(g_platform.gl.dll, name); + p = (void*)GetProcAddress(gl.dll, name); } return p; } -void* Platform::gl_context_create() +void* Win32_Platform::gl_context_create() { - HDC hdc = GetDC(g_platform.hwnd); - return g_platform.gl.create_context(hdc); + HDC hdc = GetDC(hwnd); + return gl.create_context(hdc); } -void Platform::gl_context_make_current(void* context) +void Win32_Platform::gl_context_make_current(void* context) { if (context != nullptr) { - HDC hdc = GetDC(g_platform.hwnd); - g_platform.gl.make_current(hdc, (HGLRC)context); + HDC hdc = GetDC(hwnd); + gl.make_current(hdc, (HGLRC)context); } else - g_platform.gl.make_current(NULL, NULL); + gl.make_current(NULL, NULL); } -void Platform::gl_context_destroy(void* context) +void Win32_Platform::gl_context_destroy(void* context) { - g_platform.gl.delete_context((HGLRC)context); + gl.delete_context((HGLRC)context); } -void* Platform::d3d11_get_hwnd() +void* Win32_Platform::d3d11_get_hwnd() { - return g_platform.hwnd; + return hwnd; } -Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam) +void Win32_Platform::set_clipboard(const char* text) +{ + auto len = strlen(text); + if (auto glob = GlobalAlloc(GMEM_MOVEABLE, len)) + { + if (auto data = GlobalLock(glob)) + { + memcpy(data, text, len); + GlobalUnlock(glob); + + if (OpenClipboard(nullptr)) + { + SetClipboardData(CF_TEXT, data); + CloseClipboard(); + } + } + + GlobalFree(glob); + } +} + +const char* Win32_Platform::get_clipboard() +{ + if (OpenClipboard(nullptr)) + { + HANDLE data = GetClipboardData(CF_TEXT); + if (data) + { + auto text = static_cast(GlobalLock(data)); + if (text) + clipboard = text; + GlobalUnlock(data); + } + CloseClipboard(); + } + + return clipboard.cstr(); +} + +void Win32_Platform::open_url(const char* url) +{ + auto cmd = String("start ") + url; + system(cmd.cstr()); +} + +void Win32_Platform::detect_joysticks() +{ + auto platform = ((Win32_Platform*)App::Internal::platform); + + // mark all joysticks as unnacounted for + for (int i = 0; i < Input::max_controllers; i++) + platform->joysticks[i].accounted = false; + + // check for xinput controllers + if (platform->xinput.dll) + { + for (DWORD index = 0; index < XUSER_MAX_COUNT; index++) + { + // can't get capabilities; not connected + XINPUT_CAPABILITIES xic; + if (platform->xinput.get_capabilities(index, 0, &xic) != ERROR_SUCCESS) + continue; + + // already connected + bool already_connected = false; + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = platform->joysticks[i]; + if (it.connected && it.dinstance == GUID_NULL && it.xindex == index) + { + it.accounted = true; + already_connected = true; + break; + } + } + + if (already_connected) + continue; + + // find an empty slot and mark connected + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = platform->joysticks[i]; + if (!it.connected) + { + it.connected = it.accounted = true; + it.dinstance = GUID_NULL; + it.xindex = index; + + Log::info("Connected XInput [%i]", i); + + // TODO: + // Get Product Info & Proper Name + platform->input_state->controllers[i].on_connect("Xbox Controller", true, 15, 6, 0, 0, 0); + + break; + } + } + } + } + + // call disconnect on joysticks that aren't accounted for + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = platform->joysticks[i]; + if (it.connected && !it.accounted) + { + Log::info("Disconnected [%i]", i); + platform->input_state->controllers[i].on_disconnect(); + it = Win32_Platform::Joystick(); + } + } +} + +LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + auto platform = ((Win32_Platform*)App::Internal::platform); + auto input_state = platform->input_state; + + 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; + + // Controller connected event + case WM_DEVICECHANGE: + { + // DBT_DEVNODES_CHANGED = 0x0007 + // https://docs.microsoft.com/en-us/windows/win32/devio/wm-devicechange + if (wParam == 0x0007) + platform->detect_joysticks(); + return 0; + } + + // Mouse Input + case WM_LBUTTONDOWN: + input_state->mouse.on_press(MouseButton::Left); + return 0; + + case WM_LBUTTONUP: + input_state->mouse.on_release(MouseButton::Left); + return 0; + + case WM_RBUTTONDOWN: + input_state->mouse.on_press(MouseButton::Right); + return 0; + + case WM_RBUTTONUP: + input_state->mouse.on_release(MouseButton::Right); + return 0; + + case WM_MBUTTONDOWN: + input_state->mouse.on_press(MouseButton::Middle); + return 0; + + case WM_MBUTTONUP: + input_state->mouse.on_release(MouseButton::Middle); + return 0; + + case WM_MOUSEMOVE: + input_state->mouse.on_move(Vec2f((float)((u16)lParam), (float)(lParam >> 16)), Vec2f::zero); + return 0; + + case WM_MOUSEWHEEL: + 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) + 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) + 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) + input_state->keyboard.on_release(key); + return 0; + } + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +Blah::Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam) { // scancodes switch ((lParam >> 16) & 0xFF) @@ -927,116 +1080,9 @@ Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam) return Key::Unknown; } -void Blah::win32_detect_joysticks() +Platform* Platform::try_make_platform(const Config& config) { - // mark all joysticks as unnacounted for - for (int i = 0; i < Input::max_controllers; i++) - g_platform.joysticks[i].accounted = false; - - // check for xinput controllers - if (g_platform.xinput.dll) - { - for (DWORD index = 0; index < XUSER_MAX_COUNT; index++) - { - // can't get capabilities; not connected - XINPUT_CAPABILITIES xic; - if (g_platform.xinput.get_capabilities(index, 0, &xic) != ERROR_SUCCESS) - continue; - - // already connected - bool already_connected = false; - for (int i = 0; i < Input::max_controllers; i++) - { - auto& it = g_platform.joysticks[i]; - if (it.connected && it.dinstance == GUID_NULL && it.xindex == index) - { - it.accounted = true; - already_connected = true; - break; - } - } - - if (already_connected) - continue; - - // find an empty slot and mark connected - for (int i = 0; i < Input::max_controllers; i++) - { - auto& it = g_platform.joysticks[i]; - if (!it.connected) - { - it.connected = it.accounted = true; - it.dinstance = GUID_NULL; - it.xindex = index; - - Log::info("Connected XInput [%i]", i); - - // TODO: - // Get Product Info & Proper Name - g_platform.input_state->controllers[i].on_connect("Xbox Controller", true, 15, 6, 0, 0, 0); - - break; - } - } - } - } - - // call disconnect on joysticks that aren't accounted for - for (int i = 0; i < Input::max_controllers; i++) - { - auto& it = g_platform.joysticks[i]; - if (it.connected && !it.accounted) - { - Log::info("Disconnected [%i]", i); - g_platform.input_state->controllers[i].on_disconnect(); - it = Win32Platform::Joystick(); - } - } -} - -void Platform::set_clipboard(const char* text) -{ - auto len = strlen(text); - if (auto glob = GlobalAlloc(GMEM_MOVEABLE, len)) - { - if (auto data = GlobalLock(glob)) - { - memcpy(data, text, len); - GlobalUnlock(glob); - - if (OpenClipboard(nullptr)) - { - SetClipboardData(CF_TEXT, data); - CloseClipboard(); - } - } - - GlobalFree(glob); - } -} - -const char* Platform::get_clipboard() -{ - if (OpenClipboard(nullptr)) - { - HANDLE data = GetClipboardData(CF_TEXT); - if (data) - { - auto text = static_cast(GlobalLock(data)); - if (text) - g_platform.clipboard = text; - GlobalUnlock(data); - } - CloseClipboard(); - } - - return g_platform.clipboard.cstr(); -} - -void Platform::open_url(const char* url) -{ - auto cmd = String("start ") + url; - system(cmd.cstr()); + return new Win32_Platform(); } #endif // BLAH_PLATFORM_WIN32 diff --git a/src/internal/renderer.h b/src/internal/renderer.h index 31f3454..f71d939 100644 --- a/src/internal/renderer.h +++ b/src/internal/renderer.h @@ -8,19 +8,14 @@ #include #include -#define BLAH_ASSERT_RENDERER() BLAH_ASSERT(Renderer::instance, "Renderer has not been created") - namespace Blah { class Renderer { public: - // Reference to the current Renderer in use - inline static Renderer* instance = nullptr; - - // Renderer Features - RendererFeatures features; + // Renderer Info + RendererInfo info; // Default Shader for the Batcher ShaderRef default_batcher_shader; diff --git a/src/internal/renderer_d3d11.cpp b/src/internal/renderer_d3d11.cpp index 341853c..e37f8cd 100644 --- a/src/internal/renderer_d3d11.cpp +++ b/src/internal/renderer_d3d11.cpp @@ -4,6 +4,7 @@ // Note the D3D11 Implementation is still a work-in-progress #include "renderer.h" +#include "internal.h" #include "platform.h" #include #include @@ -16,7 +17,7 @@ #include // shorthand to our internal state -#define renderer ((Renderer_D3D11*)Renderer::instance) +#define renderer ((Renderer_D3D11*)App::Internal::renderer) namespace Blah { @@ -773,7 +774,7 @@ namespace Blah desc.SampleDesc.Quality = 0; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 1; - desc.OutputWindow = (HWND)Platform::d3d11_get_hwnd(); + desc.OutputWindow = (HWND)App::Internal::platform->d3d11_get_hwnd(); desc.Windowed = true; // Creation Flags @@ -815,10 +816,10 @@ namespace Blah // create a depth backbuffer // Store Features - features.type = RendererType::D3D11; - features.instancing = true; - features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; - features.origin_bottom_left = false; + info.type = RendererType::D3D11; + info.instancing = true; + info.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + info.origin_bottom_left = false; // Print Driver Info { diff --git a/src/internal/renderer_opengl.cpp b/src/internal/renderer_opengl.cpp index 16d48ed..dc80930 100644 --- a/src/internal/renderer_opengl.cpp +++ b/src/internal/renderer_opengl.cpp @@ -1,6 +1,7 @@ #ifdef BLAH_RENDERER_OPENGL #include "renderer.h" +#include "internal.h" #include "platform.h" #include #include @@ -339,7 +340,7 @@ typedef void (APIENTRY* DEBUGPROC)(GLenum source, const void* userParam); // shorthand to our internal state -#define renderer ((Renderer_OpenGL*)Renderer::instance) +#define renderer ((Renderer_OpenGL*)App::Internal::renderer) namespace Blah { @@ -1158,16 +1159,16 @@ namespace Blah bool Renderer_OpenGL::init() { // create gl context - context = Platform::gl_context_create(); + context = App::Internal::platform->gl_context_create(); if (context == nullptr) { Log::error("Failed to create OpenGL Context"); return false; } - Platform::gl_context_make_current(context); + App::Internal::platform->gl_context_make_current(context); // bind opengl functions - #define GL_FUNC(name, ...) gl.name = (Renderer_OpenGL::Bindings::name ## Func)(Platform::gl_get_func("gl" #name)); + #define GL_FUNC(name, ...) gl.name = (Renderer_OpenGL::Bindings::name ## Func)(App::Internal::platform->gl_get_func("gl" #name)); GL_FUNCTIONS #undef GL_FUNC @@ -1198,10 +1199,10 @@ namespace Blah gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // assign info - features.type = RendererType::OpenGL; - features.instancing = true; - features.origin_bottom_left = true; - features.max_texture_size = max_texture_size; + info.type = RendererType::OpenGL; + info.instancing = true; + info.origin_bottom_left = true; + info.max_texture_size = max_texture_size; // create the default batch shader default_batcher_shader = Shader::create(opengl_batch_shader_data); @@ -1211,7 +1212,7 @@ namespace Blah void Renderer_OpenGL::shutdown() { - Platform::gl_context_destroy(context); + App::Internal::platform->gl_context_destroy(context); context = nullptr; } diff --git a/src/time.cpp b/src/time.cpp index 6843fc2..417c044 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -1,5 +1,5 @@ #include -#include "internal/platform.h" +#include "internal/internal.h" using namespace Blah; @@ -12,7 +12,9 @@ float Time::pause_timer = 0; u64 Time::get_ticks() { - return Platform::ticks(); + if (App::Internal::platform) + return App::Internal::platform->ticks(); + return 0; } void Time::pause_for(float duration) @@ -66,7 +68,7 @@ Stopwatch::Stopwatch() void Stopwatch::reset() { - start_time = Platform::ticks(); + start_time = Time::get_ticks(); } u64 Stopwatch::milliseconds() const @@ -76,5 +78,5 @@ u64 Stopwatch::milliseconds() const u64 Stopwatch::microseconds() const { - return Platform::ticks() - start_time; + return Time::get_ticks() - start_time; }