diff --git a/src/olympic/studio/modlib/include/studio/editor.hpp b/src/olympic/studio/modlib/include/studio/editor.hpp index 6b68b241..341fa2f0 100644 --- a/src/olympic/studio/modlib/include/studio/editor.hpp +++ b/src/olympic/studio/modlib/include/studio/editor.hpp @@ -6,6 +6,8 @@ #include +#include + #include "undostack.hpp" #include "widget.hpp" diff --git a/src/olympic/turbine/include/turbine/context.hpp b/src/olympic/turbine/include/turbine/context.hpp index f8da6152..bc8a8e0f 100644 --- a/src/olympic/turbine/include/turbine/context.hpp +++ b/src/olympic/turbine/include/turbine/context.hpp @@ -6,13 +6,9 @@ #include #include -#include -#include #include -#include "input.hpp" - namespace turbine { class Context; @@ -47,10 +43,5 @@ T *applicationData(Context &ctx) noexcept { return applicationDataRaw(ctx).get(); } -void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept; - -[[nodiscard]] -KeyEventHandler keyEventHandler(Context &ctx) noexcept; - } diff --git a/src/olympic/turbine/include/turbine/event.hpp b/src/olympic/turbine/include/turbine/event.hpp deleted file mode 100644 index 9f415e98..00000000 --- a/src/olympic/turbine/include/turbine/event.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -namespace turbine { - -class Context; - -using UpdateHandler = int(*)(Context&); - -// Sets event handler that sleeps for the time given in the return value. The -// sleep time is a minimum of ~16 milliseconds. -void setUpdateHandler(Context &ctx, UpdateHandler) noexcept; - -} diff --git a/src/olympic/turbine/include/turbine/gfx.hpp b/src/olympic/turbine/include/turbine/gfx.hpp index 52c15563..c5124233 100644 --- a/src/olympic/turbine/include/turbine/gfx.hpp +++ b/src/olympic/turbine/include/turbine/gfx.hpp @@ -21,11 +21,9 @@ class Drawer { virtual void draw(Context&) noexcept = 0; }; void addDrawer(Context &ctx, Drawer *cd) noexcept; -void removeDrawer(Context &ctx, Drawer *cd) noexcept; +void removeDrawer(Context &ctx, Drawer const *cd) noexcept; } -ox::Error initGfx(Context &ctx) noexcept; - ox::Error setWindowIcon(Context &ctx, ox::SpanView> const &iconPngs) noexcept; void setWindowTitle(Context &ctx, ox::StringViewCR title) noexcept; diff --git a/src/olympic/turbine/include/turbine/input.hpp b/src/olympic/turbine/include/turbine/input.hpp deleted file mode 100644 index 20667cb9..00000000 --- a/src/olympic/turbine/include/turbine/input.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include - -namespace turbine { - -enum Key { - // GBA implementation currently relies on GamePad entry order - GamePad_A = 0, - GamePad_B, - GamePad_Select, - GamePad_Start, - GamePad_Right, - GamePad_Left, - GamePad_Up, - GamePad_Down, - GamePad_R, - GamePad_L, - - Num_0, - Num_1, - Num_2, - Num_3, - Num_4, - Num_5, - Num_6, - Num_7, - Num_8, - Num_9, - - Alpha_A, - Alpha_B, - Alpha_C, - Alpha_D, - Alpha_E, - Alpha_F, - Alpha_G, - Alpha_H, - Alpha_I, - Alpha_J, - Alpha_K, - Alpha_L, - Alpha_M, - Alpha_N, - Alpha_O, - Alpha_P, - Alpha_Q, - Alpha_R, - Alpha_S, - Alpha_T, - Alpha_U, - Alpha_V, - Alpha_W, - Alpha_X, - Alpha_Y, - Alpha_Z, - - Mod_Alt, - Mod_Ctrl, - Mod_Super, - Mod_Shift, - - Escape, - - End -}; - -class Context; - -[[nodiscard]] -bool buttonDown(Context const&ctx, Key) noexcept; - -using KeyEventHandler = void(*)(Context&, Key, bool); - -} diff --git a/src/olympic/turbine/include/turbine/turbine.hpp b/src/olympic/turbine/include/turbine/turbine.hpp index 34e34a16..338cd4b3 100644 --- a/src/olympic/turbine/include/turbine/turbine.hpp +++ b/src/olympic/turbine/include/turbine/turbine.hpp @@ -8,13 +8,85 @@ #include #include "clipboard.hpp" -#include "event.hpp" #include "gfx.hpp" -#include "input.hpp" namespace turbine { +class Context; + using TimeMs = uint64_t; +using UpdateHandler = int(*)(Context&); + +enum Key { + // GBA implementation currently relies on GamePad entry order + GamePad_A = 0, + GamePad_B, + GamePad_Select, + GamePad_Start, + GamePad_Right, + GamePad_Left, + GamePad_Up, + GamePad_Down, + GamePad_R, + GamePad_L, + + Num_0, + Num_1, + Num_2, + Num_3, + Num_4, + Num_5, + Num_6, + Num_7, + Num_8, + Num_9, + + Alpha_A, + Alpha_B, + Alpha_C, + Alpha_D, + Alpha_E, + Alpha_F, + Alpha_G, + Alpha_H, + Alpha_I, + Alpha_J, + Alpha_K, + Alpha_L, + Alpha_M, + Alpha_N, + Alpha_O, + Alpha_P, + Alpha_Q, + Alpha_R, + Alpha_S, + Alpha_T, + Alpha_U, + Alpha_V, + Alpha_W, + Alpha_X, + Alpha_Y, + Alpha_Z, + + Mod_Alt, + Mod_Ctrl, + Mod_Super, + Mod_Shift, + + Escape, + + End +}; + +using KeyEventHandler = void(*)(Context&, Key, bool); + +void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept; + +[[nodiscard]] +KeyEventHandler keyEventHandler(Context &ctx) noexcept; + +[[nodiscard]] +bool buttonDown(Context const&ctx, Key) noexcept; ox::Result> init(ox::UPtr &&fs, ox::StringViewCR appName) noexcept; @@ -33,4 +105,8 @@ using ShutdownHandler = bool (*)(Context&); void setShutdownHandler(Context &ctx, ShutdownHandler handler) noexcept; +// Sets event handler that sleeps for the time given in the return value. The +// sleep time is a minimum of ~16 milliseconds. +void setUpdateHandler(Context &ctx, UpdateHandler) noexcept; + } diff --git a/src/olympic/turbine/src/gba/CMakeLists.txt b/src/olympic/turbine/src/gba/CMakeLists.txt index 3491f148..3eed0754 100644 --- a/src/olympic/turbine/src/gba/CMakeLists.txt +++ b/src/olympic/turbine/src/gba/CMakeLists.txt @@ -6,8 +6,6 @@ target_sources( Turbine-GBA PRIVATE context.cpp clipboard.cpp - event.cpp - gfx.cpp irq.arm.cpp turbine.arm.cpp turbine.cpp diff --git a/src/olympic/turbine/src/gba/context.hpp b/src/olympic/turbine/src/gba/context.hpp index 5d2ba4a8..e59eb399 100644 --- a/src/olympic/turbine/src/gba/context.hpp +++ b/src/olympic/turbine/src/gba/context.hpp @@ -6,9 +6,8 @@ #include -#include #include -#include +#include namespace turbine { diff --git a/src/olympic/turbine/src/gba/event.cpp b/src/olympic/turbine/src/gba/event.cpp deleted file mode 100644 index 52e792e6..00000000 --- a/src/olympic/turbine/src/gba/event.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include "context.hpp" - -namespace turbine { - -void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept { - ctx.updateHandler = h; -} - -void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept { - ctx.keyEventHandler = h; -} - -KeyEventHandler keyEventHandler(Context &ctx) noexcept { - return ctx.keyEventHandler; -} - -} diff --git a/src/olympic/turbine/src/gba/gfx.cpp b/src/olympic/turbine/src/gba/gfx.cpp deleted file mode 100644 index 3d0bf448..00000000 --- a/src/olympic/turbine/src/gba/gfx.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include -#include - -#include -#include -#include - -#include - -namespace turbine { - -ox::Error initGfx(Context&) noexcept { - REG_DISPCTL = teagba::DispCtl_Mode0 - | teagba::DispCtl_SpriteMap1D - | teagba::DispCtl_Obj; - // tell display to trigger vblank interrupts - REG_DISPSTAT = REG_DISPSTAT | teagba::DispStat_irq_vblank; - // enable vblank interrupt - REG_IE = REG_IE | teagba::Int_vblank; - return {}; -} - -void setWindowTitle(Context&, ox::StringViewCR) noexcept { -} - -int getScreenWidth(Context const&) noexcept { - return 240; -} - -int getScreenHeight(Context const&) noexcept { - return 160; -} - -ox::Size getScreenSize(Context const&) noexcept { - return {240, 160}; -} - -ox::Bounds getWindowBounds(Context const&) noexcept { - return {0, 0, 240, 160}; -} - -ox::Error setWindowBounds(Context&, ox::Bounds const&) noexcept { - return ox::Error(1, "setWindowBounds not supported on GBA"); -} - -} diff --git a/src/olympic/turbine/src/gba/turbine.cpp b/src/olympic/turbine/src/gba/turbine.cpp index d4e3b258..c015e036 100644 --- a/src/olympic/turbine/src/gba/turbine.cpp +++ b/src/olympic/turbine/src/gba/turbine.cpp @@ -3,8 +3,10 @@ */ #include +#include #include + #include #include #include @@ -58,6 +60,40 @@ OX_ALLOW_UNSAFE_BUFFERS_END return ox::Error(1); } +static ox::Error initGfx(Context&) noexcept { + REG_DISPCTL = teagba::DispCtl_Mode0 + | teagba::DispCtl_SpriteMap1D + | teagba::DispCtl_Obj; + // tell display to trigger vblank interrupts + REG_DISPSTAT = REG_DISPSTAT | teagba::DispStat_irq_vblank; + // enable vblank interrupt + REG_IE = REG_IE | teagba::Int_vblank; + return {}; +} + +void setWindowTitle(Context&, ox::StringViewCR) noexcept { +} + +int getScreenWidth(Context const&) noexcept { + return 240; +} + +int getScreenHeight(Context const&) noexcept { + return 160; +} + +ox::Size getScreenSize(Context const&) noexcept { + return {240, 160}; +} + +ox::Bounds getWindowBounds(Context const&) noexcept { + return {0, 0, 240, 160}; +} + +ox::Error setWindowBounds(Context&, ox::Bounds const&) noexcept { + return ox::Error(1, "setWindowBounds not supported on GBA"); +} + ox::Result> init( ox::UPtr &&fs, ox::StringViewCR appName) noexcept { auto ctx = ox::make_unique(); @@ -68,7 +104,7 @@ ox::Result> init( OX_RETURN_ERROR(initGfx(*ctx)); initTimer(); initIrq(); - return ox::UPtr(std::move(ctx)); + return ctx; } void shutdown(Context&) noexcept { @@ -89,4 +125,16 @@ void requestShutdown(Context &ctx, bool) noexcept { void setShutdownHandler(Context&, ShutdownHandler) noexcept { } +void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept { + ctx.updateHandler = h; +} + +void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept { + ctx.keyEventHandler = h; +} + +KeyEventHandler keyEventHandler(Context &ctx) noexcept { + return ctx.keyEventHandler; +} + } diff --git a/src/olympic/turbine/src/glfw/CMakeLists.txt b/src/olympic/turbine/src/glfw/CMakeLists.txt index 4eb91b4f..980c2dbf 100644 --- a/src/olympic/turbine/src/glfw/CMakeLists.txt +++ b/src/olympic/turbine/src/glfw/CMakeLists.txt @@ -5,8 +5,6 @@ target_sources( Turbine PRIVATE context.cpp clipboard.cpp - event.cpp - gfx.cpp turbine.cpp ) diff --git a/src/olympic/turbine/src/glfw/context.hpp b/src/olympic/turbine/src/glfw/context.hpp index 50e29ffc..c526b6ba 100644 --- a/src/olympic/turbine/src/glfw/context.hpp +++ b/src/olympic/turbine/src/glfw/context.hpp @@ -7,7 +7,6 @@ #include #include #include -#include #include namespace turbine { diff --git a/src/olympic/turbine/src/glfw/event.cpp b/src/olympic/turbine/src/glfw/event.cpp deleted file mode 100644 index 52e792e6..00000000 --- a/src/olympic/turbine/src/glfw/event.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include "context.hpp" - -namespace turbine { - -void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept { - ctx.updateHandler = h; -} - -void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept { - ctx.keyEventHandler = h; -} - -KeyEventHandler keyEventHandler(Context &ctx) noexcept { - return ctx.keyEventHandler; -} - -} diff --git a/src/olympic/turbine/src/glfw/gfx.cpp b/src/olympic/turbine/src/glfw/gfx.cpp deleted file mode 100644 index 767cef8a..00000000 --- a/src/olympic/turbine/src/glfw/gfx.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include -#include -#include -#if TURBINE_USE_IMGUI -#include -#include -#endif - -#include "config.hpp" - -#include - -#include "context.hpp" - -namespace turbine { - -namespace gl { - -void addDrawer(Context &ctx, Drawer *cd) noexcept { - ctx.drawers.emplace_back(cd); -} - -void removeDrawer(Context &ctx, Drawer *cd) noexcept { - for (auto i = 0u; i < ctx.drawers.size(); ++i) { - if (ctx.drawers[i] == cd) { - std::ignore = ctx.drawers.erase(i); - break; - } - } -} - -} - -static void handleGlfwError(int err, char const*desc) noexcept { - oxErrf("GLFW error ({}): {}\n", err, desc); -} - -static auto setKeyDownStatus(Context &ctx, Key key, bool down) noexcept { - ctx.keysDown &= ~(1llu << static_cast(key)); - ctx.keysDown |= static_cast(down) << static_cast(key); -} - -static void handleKeyPress(Context &ctx, int key, bool down) noexcept { - static constexpr auto keyMap = [] { - ox::Array map = {}; - for (auto i = 0u; i < 26; ++i) { - map[GLFW_KEY_A + i] = static_cast(static_cast(Key::Alpha_A) + i); - } - for (auto i = 0u; i < 10; ++i) { - map[GLFW_KEY_0 + i] = static_cast(static_cast(Key::Num_0) + i); - } - map[GLFW_KEY_LEFT_ALT] = Key::Mod_Alt; - map[GLFW_KEY_RIGHT_ALT] = Key::Mod_Alt; - map[GLFW_KEY_LEFT_CONTROL] = Key::Mod_Ctrl; - map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl; - map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super; - map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super; - map[GLFW_KEY_LEFT_SHIFT] = Key::Mod_Shift; - map[GLFW_KEY_RIGHT_SHIFT] = Key::Mod_Shift; - map[GLFW_KEY_ESCAPE] = Key::Escape; - return map; - }(); - auto const eventHandler = keyEventHandler(ctx); - auto const keyIdx = static_cast(key); - if (keyIdx < keyMap.size()) { - auto const k = keyMap[keyIdx]; - setKeyDownStatus(ctx, k, down); - if (eventHandler) { - eventHandler(ctx, k, down); - } - } -} - -static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept { -} - -static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept { - auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); - ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; -} - -static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept { - auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); - ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; - if (action == GLFW_PRESS) { - handleKeyPress(ctx, key, true); - } else if (action == GLFW_RELEASE) { - handleKeyPress(ctx, key, false); - } -} - -static void handleGlfwWindowCloseEvent(GLFWwindow *window) noexcept { - auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); - ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; - ctx.running = ctx.shutdownHandler ? !ctx.shutdownHandler(ctx) : false; - glfwSetWindowShouldClose(window, !ctx.running); - glfwPostEmptyEvent(); -} - -#if TURBINE_USE_IMGUI -static void themeImgui() noexcept { - // Dark Ruda style by Raikiri from ImThemes - auto &style = ImGui::GetStyle(); - style.Alpha = 1.0; - style.DisabledAlpha = 0.6000000238418579; - style.WindowPadding = ImVec2(8.0, 8.0); - style.WindowRounding = 0.0; - style.WindowBorderSize = 1.0; - style.WindowMinSize = ImVec2(32.0, 32.0); - style.WindowTitleAlign = ImVec2(0.0, 0.5); - style.WindowMenuButtonPosition = ImGuiDir_Left; - style.ChildRounding = 0.0; - style.ChildBorderSize = 1.0; - style.PopupRounding = 0.0; - style.PopupBorderSize = 1.0; - style.FramePadding = ImVec2(4.0, 3.0); - // custom value - style.FrameRounding = 3.0; - style.FrameBorderSize = 0.0; - style.ItemSpacing = ImVec2(8.0, 4.0); - style.ItemInnerSpacing = ImVec2(4.0, 4.0); - style.CellPadding = ImVec2(4.0, 2.0); - style.IndentSpacing = 21.0; - style.ColumnsMinSpacing = 6.0; - style.ScrollbarSize = 14.0; - style.ScrollbarRounding = 9.0; - style.GrabMinSize = 10.0; - style.GrabRounding = 4.0; - style.TabRounding = 4.0; - style.TabBorderSize = 0.0; - style.TabMinWidthForCloseButton = 0.0; - style.ColorButtonPosition = ImGuiDir_Right; - style.ButtonTextAlign = ImVec2(0.5, 0.5); - style.SelectableTextAlign = ImVec2(0.0, 0.0); - // colors - constexpr auto imVec4 = [](double r, double g, double b, double a) { - return ImVec4( - static_cast(r), - static_cast(g), - static_cast(b), - static_cast(a)); - }; - auto colors = ox::Span(style.Colors); - colors[ImGuiCol_Text] = imVec4(0.9490196108818054, 0.95686274766922, 0.9764705896377563, 1.0); - colors[ImGuiCol_TextDisabled] = imVec4(0.3568627536296844, 0.4196078479290009, 0.4666666686534882, 1.0); - colors[ImGuiCol_WindowBg] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); - colors[ImGuiCol_ChildBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0); - colors[ImGuiCol_PopupBg] = imVec4(0.0784313753247261, 0.0784313753247261, 0.0784313753247261, 0.9399999976158142); - colors[ImGuiCol_Border] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0); - colors[ImGuiCol_BorderShadow] = imVec4(0.0, 0.0, 0.0, 0.0); - colors[ImGuiCol_FrameBg] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); - colors[ImGuiCol_FrameBgHovered] = imVec4(0.1176470592617989, 0.2000000029802322, 0.2784313857555389, 1.0); - colors[ImGuiCol_FrameBgActive] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 1.0); - colors[ImGuiCol_TitleBg] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 0.6499999761581421); - colors[ImGuiCol_TitleBgActive] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0); - colors[ImGuiCol_TitleBgCollapsed] = imVec4(0.0, 0.0, 0.0, 0.5099999904632568); - colors[ImGuiCol_MenuBarBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0); - colors[ImGuiCol_ScrollbarBg] = imVec4(0.01960784383118153, 0.01960784383118153, 0.01960784383118153, 0.3899999856948853); - colors[ImGuiCol_ScrollbarGrab] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); - colors[ImGuiCol_ScrollbarGrabHovered] = imVec4(0.1764705926179886, 0.2196078449487686, 0.2470588237047195, 1.0); - colors[ImGuiCol_ScrollbarGrabActive] = imVec4(0.08627451211214066, 0.2078431397676468, 0.3098039329051971, 1.0); - colors[ImGuiCol_CheckMark] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); - colors[ImGuiCol_SliderGrab] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); - colors[ImGuiCol_SliderGrabActive] = imVec4(0.3686274588108063, 0.6078431606292725, 1.0, 1.0); - colors[ImGuiCol_Button] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); - colors[ImGuiCol_ButtonHovered] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); - colors[ImGuiCol_ButtonActive] = imVec4(0.05882352963089943, 0.529411792755127, 0.9764705896377563, 1.0); - // custom value - colors[ImGuiCol_Header] = imVec4(0.4000000029802322, 0.4470588237047195, 0.4862745225429535, 0.550000011920929); - colors[ImGuiCol_HeaderHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929); - colors[ImGuiCol_HeaderActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0); - colors[ImGuiCol_Separator] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); - colors[ImGuiCol_SeparatorHovered] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 0.7799999713897705); - colors[ImGuiCol_SeparatorActive] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 1.0); - colors[ImGuiCol_ResizeGrip] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.25); - colors[ImGuiCol_ResizeGripHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.6700000166893005); - colors[ImGuiCol_ResizeGripActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.949999988079071); - colors[ImGuiCol_Tab] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); - colors[ImGuiCol_TabHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929); - colors[ImGuiCol_TabActive] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); - colors[ImGuiCol_TabUnfocused] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); - colors[ImGuiCol_TabUnfocusedActive] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); - colors[ImGuiCol_PlotLines] = imVec4(0.6078431606292725, 0.6078431606292725, 0.6078431606292725, 1.0); - colors[ImGuiCol_PlotLinesHovered] = imVec4(1.0, 0.4274509847164154, 0.3490196168422699, 1.0); - colors[ImGuiCol_PlotHistogram] = imVec4(0.8980392217636108, 0.6980392336845398, 0.0, 1.0); - colors[ImGuiCol_PlotHistogramHovered] = imVec4(1.0, 0.6000000238418579, 0.0, 1.0); - colors[ImGuiCol_TableHeaderBg] = imVec4(0.1882352977991104, 0.1882352977991104, 0.2000000029802322, 1.0); - colors[ImGuiCol_TableBorderStrong] = imVec4(0.3098039329051971, 0.3098039329051971, 0.3490196168422699, 1.0); - colors[ImGuiCol_TableBorderLight] = imVec4(0.2274509817361832, 0.2274509817361832, 0.2470588237047195, 1.0); - colors[ImGuiCol_TableRowBg] = imVec4(0.0, 0.0, 0.0, 0.0); - colors[ImGuiCol_TableRowBgAlt] = imVec4(1.0, 1.0, 1.0, 0.05999999865889549); - colors[ImGuiCol_TextSelectedBg] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.3499999940395355); - colors[ImGuiCol_DragDropTarget] = imVec4(1.0, 1.0, 0.0, 0.8999999761581421); - colors[ImGuiCol_NavHighlight] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0); - colors[ImGuiCol_NavWindowingHighlight] = imVec4(1.0, 1.0, 1.0, 0.699999988079071); - colors[ImGuiCol_NavWindowingDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.2000000029802322); - colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355); -} -#endif - -ox::Error initGfx(Context &ctx) noexcept { - glfwSetErrorCallback(handleGlfwError); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - constexpr auto Scale = 5; - ctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx.keelCtx.appName.c_str(), nullptr, nullptr); - //ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr); - if (ctx.window == nullptr) { - return ox::Error(1, "Could not open GLFW window"); - } - glfwSetCursorPosCallback(ctx.window, handleGlfwCursorPosEvent); - glfwSetMouseButtonCallback(ctx.window, handleGlfwMouseButtonEvent); - glfwSetKeyCallback(ctx.window, handleGlfwKeyEvent); - glfwSetWindowCloseCallback(ctx.window, handleGlfwWindowCloseEvent); - glfwSetWindowUserPointer(ctx.window, &ctx); - glfwMakeContextCurrent(ctx.window); - if (!gladLoadGLES2Loader(reinterpret_cast(glfwGetProcAddress))) { - return ox::Error(2, "Could not init Glad"); - } -#if TURBINE_USE_IMGUI - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - auto &io = ImGui::GetIO(); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; - //io.MouseDrawCursor = true; - ImGui_ImplGlfw_InitForOpenGL(ctx.window, true); - ImGui_ImplOpenGL3_Init(); - io.IniFilename = nullptr; - themeImgui(); -#endif - return {}; -} - -struct IconData { - std::vector pixels; - int w{}, h{}; -}; - -[[nodiscard]] -[[maybe_unused]] -static ox::Result toGlfwImgPixels(ox::SpanView const &iconPng) noexcept { - ox::Result out; - unsigned w{}, h{}; - if (lodepng::decode(out.value.pixels, w, h, iconPng.data(), iconPng.size())) { - return ox::Error{1, "unable to decode window icon PNG data"}; - } - out.value.w = static_cast(w); - out.value.h = static_cast(h); - return out; -} - -ox::Error setWindowIcon(Context &ctx, ox::SpanView> const &iconPngs) noexcept { - if constexpr(ox::defines::OS != ox::OS::Darwin) { - ox::Vector src; - ox::Vector imgs; - for (auto const &iconPng : iconPngs) { - OX_RETURN_ERROR(toGlfwImgPixels(iconPng).moveTo(src.emplace_back())); - auto &icon = *src.back().unwrap(); - imgs.emplace_back(GLFWimage{ - .width = icon.w, - .height = icon.h, - .pixels = icon.pixels.data(), - }); - } - glfwSetWindowIcon(ctx.window, static_cast(imgs.size()), imgs.data()); - } - return {}; -} - -void setWindowTitle(Context &ctx, ox::StringViewCR title) noexcept { - auto cstr = ox_malloca(title.bytes() + 1, char); - OX_ALLOW_UNSAFE_BUFFERS_BEGIN - ox::strncpy(cstr.get(), title.data(), title.bytes()); - OX_ALLOW_UNSAFE_BUFFERS_END - glfwSetWindowTitle(ctx.window, cstr.get()); -} - -void focusWindow(Context &ctx) noexcept { - glfwFocusWindow(ctx.window); -} - -int getScreenWidth(Context const &ctx) noexcept { - int w = 0, h = 0; - glfwGetFramebufferSize(ctx.window, &w, &h); - return w; -} - -int getScreenHeight(Context const &ctx) noexcept { - int w = 0, h = 0; - glfwGetFramebufferSize(ctx.window, &w, &h); - return h; -} - -ox::Size getScreenSize(Context const &ctx) noexcept { - int w = 0, h = 0; - glfwGetFramebufferSize(ctx.window, &w, &h); - return {w, h}; -} - -ox::Bounds getWindowBounds(Context const &ctx) noexcept { - ox::Bounds bnds; - glfwGetWindowPos(ctx.window, &bnds.x, &bnds.y); - glfwGetWindowSize(ctx.window, &bnds.width, &bnds.height); - return bnds; -} - -ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept { - glfwSetWindowPos(ctx.window, bnds.x, bnds.y); - glfwSetWindowSize(ctx.window, bnds.width, bnds.height); - return {}; -} - -void setRefreshWithin(Context &ctx, int ms) noexcept { - ctx.refreshWithinMs = ox::min(ms, ctx.refreshWithinMs); -} - -} diff --git a/src/olympic/turbine/src/glfw/turbine.cpp b/src/olympic/turbine/src/glfw/turbine.cpp index b7b0efe9..53666d81 100644 --- a/src/olympic/turbine/src/glfw/turbine.cpp +++ b/src/olympic/turbine/src/glfw/turbine.cpp @@ -2,7 +2,9 @@ * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. */ +#include #include +#include #if TURBINE_USE_IMGUI #include #include @@ -17,6 +19,207 @@ namespace turbine { +namespace gl { + +void addDrawer(Context &ctx, Drawer *cd) noexcept { + ctx.drawers.emplace_back(cd); +} + +void removeDrawer(Context &ctx, Drawer const *cd) noexcept { + for (auto i = 0u; i < ctx.drawers.size(); ++i) { + if (ctx.drawers[i] == cd) { + std::ignore = ctx.drawers.erase(i); + break; + } + } +} + +} + +#if TURBINE_USE_IMGUI +static void themeImgui() noexcept { + // Dark Ruda style by Raikiri from ImThemes + auto &style = ImGui::GetStyle(); + style.Alpha = 1.0; + style.DisabledAlpha = 0.6000000238418579; + style.WindowPadding = ImVec2(8.0, 8.0); + style.WindowRounding = 0.0; + style.WindowBorderSize = 1.0; + style.WindowMinSize = ImVec2(32.0, 32.0); + style.WindowTitleAlign = ImVec2(0.0, 0.5); + style.WindowMenuButtonPosition = ImGuiDir_Left; + style.ChildRounding = 0.0; + style.ChildBorderSize = 1.0; + style.PopupRounding = 0.0; + style.PopupBorderSize = 1.0; + style.FramePadding = ImVec2(4.0, 3.0); + // custom value + style.FrameRounding = 3.0; + style.FrameBorderSize = 0.0; + style.ItemSpacing = ImVec2(8.0, 4.0); + style.ItemInnerSpacing = ImVec2(4.0, 4.0); + style.CellPadding = ImVec2(4.0, 2.0); + style.IndentSpacing = 21.0; + style.ColumnsMinSpacing = 6.0; + style.ScrollbarSize = 14.0; + style.ScrollbarRounding = 9.0; + style.GrabMinSize = 10.0; + style.GrabRounding = 4.0; + style.TabRounding = 4.0; + style.TabBorderSize = 0.0; + style.TabMinWidthForCloseButton = 0.0; + style.ColorButtonPosition = ImGuiDir_Right; + style.ButtonTextAlign = ImVec2(0.5, 0.5); + style.SelectableTextAlign = ImVec2(0.0, 0.0); + // colors + constexpr auto imVec4 = [](double r, double g, double b, double a) { + return ImVec4( + static_cast(r), + static_cast(g), + static_cast(b), + static_cast(a)); + }; + auto colors = ox::Span(style.Colors); + colors[ImGuiCol_Text] = imVec4(0.9490196108818054, 0.95686274766922, 0.9764705896377563, 1.0); + colors[ImGuiCol_TextDisabled] = imVec4(0.3568627536296844, 0.4196078479290009, 0.4666666686534882, 1.0); + colors[ImGuiCol_WindowBg] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); + colors[ImGuiCol_ChildBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0); + colors[ImGuiCol_PopupBg] = imVec4(0.0784313753247261, 0.0784313753247261, 0.0784313753247261, 0.9399999976158142); + colors[ImGuiCol_Border] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0); + colors[ImGuiCol_BorderShadow] = imVec4(0.0, 0.0, 0.0, 0.0); + colors[ImGuiCol_FrameBg] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); + colors[ImGuiCol_FrameBgHovered] = imVec4(0.1176470592617989, 0.2000000029802322, 0.2784313857555389, 1.0); + colors[ImGuiCol_FrameBgActive] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 1.0); + colors[ImGuiCol_TitleBg] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 0.6499999761581421); + colors[ImGuiCol_TitleBgActive] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0); + colors[ImGuiCol_TitleBgCollapsed] = imVec4(0.0, 0.0, 0.0, 0.5099999904632568); + colors[ImGuiCol_MenuBarBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0); + colors[ImGuiCol_ScrollbarBg] = imVec4(0.01960784383118153, 0.01960784383118153, 0.01960784383118153, 0.3899999856948853); + colors[ImGuiCol_ScrollbarGrab] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); + colors[ImGuiCol_ScrollbarGrabHovered] = imVec4(0.1764705926179886, 0.2196078449487686, 0.2470588237047195, 1.0); + colors[ImGuiCol_ScrollbarGrabActive] = imVec4(0.08627451211214066, 0.2078431397676468, 0.3098039329051971, 1.0); + colors[ImGuiCol_CheckMark] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); + colors[ImGuiCol_SliderGrab] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); + colors[ImGuiCol_SliderGrabActive] = imVec4(0.3686274588108063, 0.6078431606292725, 1.0, 1.0); + colors[ImGuiCol_Button] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); + colors[ImGuiCol_ButtonHovered] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0); + colors[ImGuiCol_ButtonActive] = imVec4(0.05882352963089943, 0.529411792755127, 0.9764705896377563, 1.0); + // custom value + colors[ImGuiCol_Header] = imVec4(0.4000000029802322, 0.4470588237047195, 0.4862745225429535, 0.550000011920929); + colors[ImGuiCol_HeaderHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929); + colors[ImGuiCol_HeaderActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0); + colors[ImGuiCol_Separator] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); + colors[ImGuiCol_SeparatorHovered] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 0.7799999713897705); + colors[ImGuiCol_SeparatorActive] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 1.0); + colors[ImGuiCol_ResizeGrip] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.25); + colors[ImGuiCol_ResizeGripHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.6700000166893005); + colors[ImGuiCol_ResizeGripActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.949999988079071); + colors[ImGuiCol_Tab] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); + colors[ImGuiCol_TabHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929); + colors[ImGuiCol_TabActive] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0); + colors[ImGuiCol_TabUnfocused] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); + colors[ImGuiCol_TabUnfocusedActive] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0); + colors[ImGuiCol_PlotLines] = imVec4(0.6078431606292725, 0.6078431606292725, 0.6078431606292725, 1.0); + colors[ImGuiCol_PlotLinesHovered] = imVec4(1.0, 0.4274509847164154, 0.3490196168422699, 1.0); + colors[ImGuiCol_PlotHistogram] = imVec4(0.8980392217636108, 0.6980392336845398, 0.0, 1.0); + colors[ImGuiCol_PlotHistogramHovered] = imVec4(1.0, 0.6000000238418579, 0.0, 1.0); + colors[ImGuiCol_TableHeaderBg] = imVec4(0.1882352977991104, 0.1882352977991104, 0.2000000029802322, 1.0); + colors[ImGuiCol_TableBorderStrong] = imVec4(0.3098039329051971, 0.3098039329051971, 0.3490196168422699, 1.0); + colors[ImGuiCol_TableBorderLight] = imVec4(0.2274509817361832, 0.2274509817361832, 0.2470588237047195, 1.0); + colors[ImGuiCol_TableRowBg] = imVec4(0.0, 0.0, 0.0, 0.0); + colors[ImGuiCol_TableRowBgAlt] = imVec4(1.0, 1.0, 1.0, 0.05999999865889549); + colors[ImGuiCol_TextSelectedBg] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.3499999940395355); + colors[ImGuiCol_DragDropTarget] = imVec4(1.0, 1.0, 0.0, 0.8999999761581421); + colors[ImGuiCol_NavHighlight] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0); + colors[ImGuiCol_NavWindowingHighlight] = imVec4(1.0, 1.0, 1.0, 0.699999988079071); + colors[ImGuiCol_NavWindowingDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.2000000029802322); + colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355); +} +#endif + +struct IconData { + std::vector pixels; + int w{}, h{}; +}; + +[[nodiscard]] +[[maybe_unused]] +static ox::Result toGlfwImgPixels(ox::SpanView const &iconPng) noexcept { + ox::Result out; + unsigned w{}, h{}; + if (lodepng::decode(out.value.pixels, w, h, iconPng.data(), iconPng.size())) { + return ox::Error{1, "unable to decode window icon PNG data"}; + } + out.value.w = static_cast(w); + out.value.h = static_cast(h); + return out; +} + +ox::Error setWindowIcon(Context &ctx, ox::SpanView> const &iconPngs) noexcept { + if constexpr(ox::defines::OS != ox::OS::Darwin) { + ox::Vector src; + ox::Vector imgs; + for (auto const &iconPng : iconPngs) { + OX_RETURN_ERROR(toGlfwImgPixels(iconPng).moveTo(src.emplace_back())); + auto &icon = *src.back().unwrap(); + imgs.emplace_back(GLFWimage{ + .width = icon.w, + .height = icon.h, + .pixels = icon.pixels.data(), + }); + } + glfwSetWindowIcon(ctx.window, static_cast(imgs.size()), imgs.data()); + } + return {}; +} + +void setWindowTitle(Context &ctx, ox::StringViewCR title) noexcept { + auto cstr = ox_malloca(title.bytes() + 1, char); + OX_ALLOW_UNSAFE_BUFFERS_BEGIN + ox::strncpy(cstr.get(), title.data(), title.bytes()); + OX_ALLOW_UNSAFE_BUFFERS_END + glfwSetWindowTitle(ctx.window, cstr.get()); +} + +void focusWindow(Context &ctx) noexcept { + glfwFocusWindow(ctx.window); +} + +int getScreenWidth(Context const &ctx) noexcept { + int w = 0, h = 0; + glfwGetFramebufferSize(ctx.window, &w, &h); + return w; +} + +int getScreenHeight(Context const &ctx) noexcept { + int w = 0, h = 0; + glfwGetFramebufferSize(ctx.window, &w, &h); + return h; +} + +ox::Size getScreenSize(Context const &ctx) noexcept { + int w = 0, h = 0; + glfwGetFramebufferSize(ctx.window, &w, &h); + return {w, h}; +} + +ox::Bounds getWindowBounds(Context const &ctx) noexcept { + ox::Bounds bnds; + glfwGetWindowPos(ctx.window, &bnds.x, &bnds.y); + glfwGetWindowSize(ctx.window, &bnds.width, &bnds.height); + return bnds; +} + +ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept { + glfwSetWindowPos(ctx.window, bnds.x, bnds.y); + glfwSetWindowSize(ctx.window, bnds.width, bnds.height); + return {}; +} + +void setRefreshWithin(Context &ctx, int ms) noexcept { + ctx.refreshWithinMs = ox::min(ms, ctx.refreshWithinMs); +} + static void draw(Context &ctx) noexcept { // draw start #if TURBINE_USE_IMGUI @@ -35,11 +238,77 @@ static void draw(Context &ctx) noexcept { glfwSwapBuffers(ctx.window); } -static void draw(GLFWwindow *window, int, int) noexcept { +static void handleGlfwResize(GLFWwindow *window, int, int) noexcept { auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); draw(ctx); } +static void handleGlfwError(int const err, char const *desc) noexcept { + oxErrf("GLFW error ({}): {}\n", err, desc); +} + +static auto setKeyDownStatus(Context &ctx, Key const key, bool const down) noexcept { + ctx.keysDown &= ~(1llu << static_cast(key)); + ctx.keysDown |= static_cast(down) << static_cast(key); +} + +static void handleKeyPress(Context &ctx, int const key, bool const down) noexcept { + static constexpr auto keyMap = [] { + ox::Array map = {}; + for (auto i = 0u; i < 26; ++i) { + map[GLFW_KEY_A + i] = static_cast(static_cast(Key::Alpha_A) + i); + } + for (auto i = 0u; i < 10; ++i) { + map[GLFW_KEY_0 + i] = static_cast(static_cast(Key::Num_0) + i); + } + map[GLFW_KEY_LEFT_ALT] = Key::Mod_Alt; + map[GLFW_KEY_RIGHT_ALT] = Key::Mod_Alt; + map[GLFW_KEY_LEFT_CONTROL] = Key::Mod_Ctrl; + map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl; + map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super; + map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super; + map[GLFW_KEY_LEFT_SHIFT] = Key::Mod_Shift; + map[GLFW_KEY_RIGHT_SHIFT] = Key::Mod_Shift; + map[GLFW_KEY_ESCAPE] = Key::Escape; + return map; + }(); + auto const eventHandler = keyEventHandler(ctx); + auto const keyIdx = static_cast(key); + if (keyIdx < keyMap.size()) { + auto const k = keyMap[keyIdx]; + setKeyDownStatus(ctx, k, down); + if (eventHandler) { + eventHandler(ctx, k, down); + } + } +} + +static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept { +} + +static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept { + auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); + ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; +} + +static void handleGlfwKeyEvent(GLFWwindow *window, int const key, int, int const action, int) noexcept { + auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); + ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; + if (action == GLFW_PRESS) { + handleKeyPress(ctx, key, true); + } else if (action == GLFW_RELEASE) { + handleKeyPress(ctx, key, false); + } +} + +static void handleGlfwWindowCloseEvent(GLFWwindow *window) noexcept { + auto &ctx = *static_cast(glfwGetWindowUserPointer(window)); + ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod; + ctx.running = ctx.shutdownHandler ? !ctx.shutdownHandler(ctx) : false; + glfwSetWindowShouldClose(window, !ctx.running); + glfwPostEmptyEvent(); +} + ox::Result> init( ox::UPtr &&fs, ox::StringViewCR appName) noexcept { auto ctx = ox::make_unique(); @@ -47,11 +316,44 @@ ox::Result> init( using namespace std::chrono; ctx->startTime = static_cast( duration_cast(system_clock::now().time_since_epoch()).count()); - glfwInit(); - OX_RETURN_ERROR(initGfx(*ctx)); - glfwSetWindowSizeCallback(ctx->window, draw); ctx->mandatoryRefreshPeriodEnd = ticksMs(*ctx) + config::MandatoryRefreshPeriod; - return ox::UPtr(ctx.release()); + // init GLFW context + glfwInit(); + glfwSetErrorCallback(handleGlfwError); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + constexpr auto Scale = 5; + ctx->window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr); + //ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr); + if (ctx->window == nullptr) { + return ox::Error(1, "Could not open GLFW window"); + } + glfwSetCursorPosCallback(ctx->window, handleGlfwCursorPosEvent); + glfwSetMouseButtonCallback(ctx->window, handleGlfwMouseButtonEvent); + glfwSetKeyCallback(ctx->window, handleGlfwKeyEvent); + glfwSetWindowCloseCallback(ctx->window, handleGlfwWindowCloseEvent); + glfwSetWindowSizeCallback(ctx->window, handleGlfwResize); + glfwSetWindowUserPointer(ctx->window, ctx.get()); + glfwMakeContextCurrent(ctx->window); + if (!gladLoadGLES2Loader(reinterpret_cast(glfwGetProcAddress))) { + return ox::Error(2, "Could not init Glad"); + } + // init ImGui +#if TURBINE_USE_IMGUI + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + auto &io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + //io.MouseDrawCursor = true; + ImGui_ImplGlfw_InitForOpenGL(ctx->window, true); + ImGui_ImplOpenGL3_Init(); + io.IniFilename = nullptr; + themeImgui(); +#endif + return ctx; } static void tickFps(Context &ctx, uint64_t const nowMs) noexcept { @@ -132,4 +434,16 @@ void setShutdownHandler(Context &ctx, ShutdownHandler const handler) noexcept { ctx.shutdownHandler = handler; } +void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept { + ctx.updateHandler = h; +} + +void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept { + ctx.keyEventHandler = h; +} + +KeyEventHandler keyEventHandler(Context &ctx) noexcept { + return ctx.keyEventHandler; +} + }