[turbine] Consolidate some files that didn't have sensible distinctions
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Gary Talent 2025-05-17 02:47:21 -05:00
parent 3ccadba2f5
commit 5ba0bcf963
16 changed files with 450 additions and 543 deletions

View File

@ -6,6 +6,8 @@
#include <ox/event/signal.hpp>
#include <turbine/turbine.hpp>
#include "undostack.hpp"
#include "widget.hpp"

View File

@ -6,13 +6,9 @@
#include <ox/fs/fs.hpp>
#include <ox/model/desctypes.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/size.hpp>
#include <keel/context.hpp>
#include "input.hpp"
namespace turbine {
class Context;
@ -47,10 +43,5 @@ T *applicationData(Context &ctx) noexcept {
return applicationDataRaw(ctx).get<T>();
}
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
[[nodiscard]]
KeyEventHandler keyEventHandler(Context &ctx) noexcept;
}

View File

@ -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;
}

View File

@ -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<ox::SpanView<uint8_t>> const &iconPngs) noexcept;
void setWindowTitle(Context &ctx, ox::StringViewCR title) noexcept;

View File

@ -1,79 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/defines.hpp>
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);
}

View File

@ -8,13 +8,85 @@
#include <ox/fs/fs.hpp>
#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<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> &&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;
}

View File

@ -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

View File

@ -6,9 +6,8 @@
#include <keel/context.hpp>
#include <turbine/clipboard.hpp>
#include <turbine/context.hpp>
#include <turbine/event.hpp>
#include <turbine/turbine.hpp>
namespace turbine {

View File

@ -1,23 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <turbine/event.hpp>
#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;
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/size.hpp>
#include <ox/std/stringview.hpp>
#include <teagba/addresses.hpp>
#include <teagba/gfx.hpp>
#include <teagba/irq.hpp>
#include <turbine/context.hpp>
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");
}
}

View File

@ -3,8 +3,10 @@
*/
#include <teagba/addresses.hpp>
#include <teagba/gfx.hpp>
#include <teagba/irq.hpp>
#include <keel/keel.hpp>
#include <turbine/context.hpp>
#include <turbine/gfx.hpp>
@ -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<ox::UPtr<Context>> init(
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept {
auto ctx = ox::make_unique<Context>();
@ -68,7 +104,7 @@ ox::Result<ox::UPtr<Context>> init(
OX_RETURN_ERROR(initGfx(*ctx));
initTimer();
initIrq();
return ox::UPtr<Context>(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;
}
}

View File

@ -5,8 +5,6 @@ target_sources(
Turbine PRIVATE
context.cpp
clipboard.cpp
event.cpp
gfx.cpp
turbine.cpp
)

View File

@ -7,7 +7,6 @@
#include <turbine/clipboard.hpp>
#include <turbine/context.hpp>
#include <turbine/gfx.hpp>
#include <turbine/event.hpp>
#include <turbine/turbine.hpp>
namespace turbine {

View File

@ -1,23 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <turbine/event.hpp>
#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;
}
}

View File

@ -1,324 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <lodepng.h>
#if TURBINE_USE_IMGUI
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#endif
#include "config.hpp"
#include <ox/std/span.hpp>
#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<int>(key));
ctx.keysDown |= static_cast<uint64_t>(down) << static_cast<int>(key);
}
static void handleKeyPress(Context &ctx, int key, bool down) noexcept {
static constexpr auto keyMap = [] {
ox::Array<Key, GLFW_KEY_LAST> map = {};
for (auto i = 0u; i < 26; ++i) {
map[GLFW_KEY_A + i] = static_cast<Key>(static_cast<unsigned>(Key::Alpha_A) + i);
}
for (auto i = 0u; i < 10; ++i) {
map[GLFW_KEY_0 + i] = static_cast<Key>(static_cast<unsigned>(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<std::size_t>(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<Context*>(glfwGetWindowUserPointer(window));
ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod;
}
static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept {
auto &ctx = *static_cast<Context*>(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<Context*>(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<float>(r),
static_cast<float>(g),
static_cast<float>(b),
static_cast<float>(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<GLADloadproc>(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<uint8_t> pixels;
int w{}, h{};
};
[[nodiscard]]
[[maybe_unused]]
static ox::Result<IconData> toGlfwImgPixels(ox::SpanView<uint8_t> const &iconPng) noexcept {
ox::Result<IconData> 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<int>(w);
out.value.h = static_cast<int>(h);
return out;
}
ox::Error setWindowIcon(Context &ctx, ox::SpanView<ox::SpanView<uint8_t>> const &iconPngs) noexcept {
if constexpr(ox::defines::OS != ox::OS::Darwin) {
ox::Vector<IconData, 8> src;
ox::Vector<GLFWimage, 8> 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<int>(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);
}
}

View File

@ -2,7 +2,9 @@
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <lodepng.h>
#if TURBINE_USE_IMGUI
#include <imgui_impl_opengl3.h>
#include <imgui_impl_glfw.h>
@ -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<float>(r),
static_cast<float>(g),
static_cast<float>(b),
static_cast<float>(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<uint8_t> pixels;
int w{}, h{};
};
[[nodiscard]]
[[maybe_unused]]
static ox::Result<IconData> toGlfwImgPixels(ox::SpanView<uint8_t> const &iconPng) noexcept {
ox::Result<IconData> 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<int>(w);
out.value.h = static_cast<int>(h);
return out;
}
ox::Error setWindowIcon(Context &ctx, ox::SpanView<ox::SpanView<uint8_t>> const &iconPngs) noexcept {
if constexpr(ox::defines::OS != ox::OS::Darwin) {
ox::Vector<IconData, 8> src;
ox::Vector<GLFWimage, 8> 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<int>(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<Context*>(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<int>(key));
ctx.keysDown |= static_cast<uint64_t>(down) << static_cast<int>(key);
}
static void handleKeyPress(Context &ctx, int const key, bool const down) noexcept {
static constexpr auto keyMap = [] {
ox::Array<Key, GLFW_KEY_LAST> map = {};
for (auto i = 0u; i < 26; ++i) {
map[GLFW_KEY_A + i] = static_cast<Key>(static_cast<unsigned>(Key::Alpha_A) + i);
}
for (auto i = 0u; i < 10; ++i) {
map[GLFW_KEY_0 + i] = static_cast<Key>(static_cast<unsigned>(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<std::size_t>(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<Context*>(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<Context*>(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<Context*>(glfwGetWindowUserPointer(window));
ctx.mandatoryRefreshPeriodEnd = ticksMs(ctx) + config::MandatoryRefreshPeriod;
ctx.running = ctx.shutdownHandler ? !ctx.shutdownHandler(ctx) : false;
glfwSetWindowShouldClose(window, !ctx.running);
glfwPostEmptyEvent();
}
ox::Result<ox::UPtr<Context>> init(
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept {
auto ctx = ox::make_unique<Context>();
@ -47,11 +316,44 @@ ox::Result<ox::UPtr<Context>> init(
using namespace std::chrono;
ctx->startTime = static_cast<TimeMs>(
duration_cast<milliseconds>(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<Context>(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<GLADloadproc>(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;
}
}