[keel,nostalgia,studio,turbine] Cleanup structure of Turbine
This commit is contained in:
@@ -1,35 +1 @@
|
||||
add_library(
|
||||
Turbine
|
||||
event.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(gba)
|
||||
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
add_subdirectory(glfw)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
Turbine PUBLIC
|
||||
Keel
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
clipboard.hpp
|
||||
config.hpp
|
||||
context.hpp
|
||||
event.hpp
|
||||
gfx.hpp
|
||||
input.hpp
|
||||
turbine.hpp
|
||||
DESTINATION
|
||||
include/turbine
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
Turbine
|
||||
DESTINATION
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
add_subdirectory(src)
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::String getClipboardText(class Context &ctx) noexcept;
|
||||
|
||||
void setClipboardText(class Context &ctx, ox::CRStringView text) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void setClipboardObject([[maybe_unused]] class Context &ctx, [[maybe_unused]] ox::UniquePtr<T> obj) noexcept {
|
||||
ctx.clipboard = std::move(obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T*> getClipboardObject([[maybe_unused]] class Context &ctx) noexcept {
|
||||
if (ctx.clipboard && ctx.clipboard->typeMatch(T::TypeName, T::TypeVersion)) {
|
||||
return static_cast<T*>(ctx.clipboard.get());
|
||||
}
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<imgui.h>)
|
||||
#endif
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace turbine::config {
|
||||
|
||||
constexpr auto ImGuiEnabled =
|
||||
#if __has_include(<imgui.h>)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
constexpr auto GbaEventLoopTimerBased = false;
|
||||
constexpr auto GbaTimerBits = 32;
|
||||
constexpr auto GlFpsPrint = false;
|
||||
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 "event.hpp"
|
||||
#include "input.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class BaseClipboardObject {
|
||||
public:
|
||||
virtual ~BaseClipboardObject() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::String typeId() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto typeMatch(auto name, auto version) const noexcept {
|
||||
return typeId() == ox::buildTypeId(name, version);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ClipboardObject: public BaseClipboardObject {
|
||||
[[nodiscard]]
|
||||
ox::String typeId() const noexcept final {
|
||||
return ox::buildTypeId(T::TypeName, T::TypeVersion);
|
||||
}
|
||||
};
|
||||
|
||||
void shutdown(Context &ctx) noexcept;
|
||||
|
||||
// User Input Output
|
||||
class Context {
|
||||
friend constexpr void setApplicationData(Context &ctx, void *applicationData) noexcept;
|
||||
template<typename T>
|
||||
friend constexpr T *applicationData(Context &ctx) noexcept;
|
||||
friend void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept;
|
||||
friend constexpr void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
|
||||
friend constexpr KeyEventHandler keyEventHandler(Context &ctx) noexcept;
|
||||
|
||||
public:
|
||||
UpdateHandler updateHandler = [](Context&) -> int {return 0;};
|
||||
ox::UPtr<BaseClipboardObject> clipboard;
|
||||
keel::Context keelCtx;
|
||||
|
||||
protected:
|
||||
KeyEventHandler m_keyEventHandler = nullptr;
|
||||
void *m_applicationData = nullptr;
|
||||
|
||||
Context() noexcept = default;
|
||||
|
||||
public:
|
||||
Context(Context &other) noexcept = delete;
|
||||
Context(const Context &other) noexcept = delete;
|
||||
Context(const Context &&other) noexcept = delete;
|
||||
|
||||
virtual inline ~Context() noexcept {
|
||||
shutdown(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
constexpr auto *rom(Context &ctx) noexcept {
|
||||
return ctx.keelCtx.rom.get();
|
||||
}
|
||||
|
||||
constexpr const auto *rom(const Context &ctx) noexcept {
|
||||
return ctx.keelCtx.rom.get();
|
||||
}
|
||||
|
||||
constexpr void setApplicationData(Context &ctx, void *applicationData) noexcept {
|
||||
ctx.m_applicationData = applicationData;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *applicationData(Context &ctx) noexcept {
|
||||
return static_cast<T*>(ctx.m_applicationData);
|
||||
}
|
||||
|
||||
constexpr void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept {
|
||||
ctx.m_keyEventHandler = h;
|
||||
}
|
||||
|
||||
constexpr KeyEventHandler keyEventHandler(Context &ctx) noexcept {
|
||||
return ctx.m_keyEventHandler;
|
||||
}
|
||||
|
||||
void setConstantRefresh(Context &ctx, bool r) noexcept;
|
||||
|
||||
}
|
||||
|
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 "../context.hpp"
|
||||
|
||||
namespace turbine::gba {
|
||||
|
||||
class Context: public turbine::Context {
|
||||
public:
|
||||
bool running = true;
|
||||
|
||||
Context() noexcept = default;
|
||||
Context(Context &other) noexcept = delete;
|
||||
Context(const Context &other) noexcept = delete;
|
||||
Context(const Context &&other) noexcept = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
target_sources(
|
||||
Turbine PRIVATE
|
||||
clipboard.cpp
|
||||
gfx.cpp
|
||||
turbine.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
Turbine PUBLIC
|
||||
glad
|
||||
glfw
|
||||
imgui
|
||||
)
|
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::String getClipboardText(Context &ctx) noexcept {
|
||||
auto &gctx = static_cast<GlfwContext&>(ctx);
|
||||
return ox::String(glfwGetClipboardString(gctx.window));
|
||||
}
|
||||
|
||||
void setClipboardText(Context &ctx, ox::CRStringView text) noexcept {
|
||||
auto &gctx = static_cast<GlfwContext&>(ctx);
|
||||
auto cstr = ox_malloca(text.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), text.data(), text.bytes());
|
||||
glfwSetClipboardString(gctx.window, cstr.get());
|
||||
}
|
||||
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
struct GlfwContext: public turbine::Context {
|
||||
int uninterruptedRefreshes = 3;
|
||||
ox::UPtr<BaseClipboardObject> clipboard;
|
||||
struct GLFWwindow *window = nullptr;
|
||||
// sets screen refresh to constant instead of only on event
|
||||
bool constantRefresh = true;
|
||||
ox::Vector<gl::Drawer*, 5> drawers;
|
||||
int64_t startTime = 0;
|
||||
uint64_t wakeupTime = 0;
|
||||
uint64_t keysDown = 0;
|
||||
uint64_t prevFpsCheckTime = 0;
|
||||
uint64_t draws = 0;
|
||||
};
|
||||
|
||||
}
|
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
#include "../config.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
static void draw(GlfwContext &gctx) noexcept {
|
||||
// draw start
|
||||
if constexpr(turbine::config::ImGuiEnabled) {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
for (auto d : gctx.drawers) {
|
||||
d->draw(gctx);
|
||||
}
|
||||
if constexpr(turbine::config::ImGuiEnabled) {
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
// draw end
|
||||
glfwSwapBuffers(gctx.window);
|
||||
}
|
||||
|
||||
static void draw(GLFWwindow *window, int, int) noexcept {
|
||||
auto &gctx = *static_cast<GlfwContext*>(glfwGetWindowUserPointer(window));
|
||||
draw(gctx);
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
||||
auto ctx = ox::make_unique<GlfwContext>();
|
||||
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||
using namespace std::chrono;
|
||||
ctx->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
glfwInit();
|
||||
oxReturnError(initGfx(*ctx));
|
||||
glfwSetWindowSizeCallback(ctx->window, draw);
|
||||
return ox::UPtr<Context>(ctx.release());
|
||||
}
|
||||
|
||||
static void tickFps(GlfwContext &gctx, uint64_t nowMs) noexcept {
|
||||
++gctx.draws;
|
||||
if (gctx.draws >= 500) {
|
||||
const auto duration = static_cast<double>(nowMs - gctx.prevFpsCheckTime) / 1000.0;
|
||||
const auto fps = static_cast<int>(static_cast<double>(gctx.draws) / duration);
|
||||
if constexpr(config::GlFpsPrint) {
|
||||
oxOutf("FPS: {}\n", fps);
|
||||
}
|
||||
oxTracef("turbine.fps", "FPS: {}", fps);
|
||||
gctx.prevFpsCheckTime = nowMs;
|
||||
gctx.draws = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
auto &gctx = static_cast<GlfwContext&>(ctx);
|
||||
int sleepTime = 0;
|
||||
while (!glfwWindowShouldClose(gctx.window)) {
|
||||
glfwPollEvents();
|
||||
const auto ticks = ticksMs(gctx);
|
||||
if (gctx.wakeupTime <= ticks) {
|
||||
sleepTime = gctx.updateHandler(gctx);
|
||||
if (sleepTime >= 0) {
|
||||
gctx.wakeupTime = ticks + static_cast<unsigned>(sleepTime);
|
||||
} else {
|
||||
gctx.wakeupTime = ~uint64_t(0);
|
||||
}
|
||||
} else {
|
||||
sleepTime = 10;
|
||||
}
|
||||
tickFps(gctx, ticks);
|
||||
draw(gctx);
|
||||
if (!gctx.constantRefresh) {
|
||||
if (gctx.uninterruptedRefreshes) {
|
||||
--gctx.uninterruptedRefreshes;
|
||||
} else {
|
||||
glfwWaitEventsTimeout(sleepTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
shutdown(gctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
void shutdown(Context &ctx) noexcept {
|
||||
auto &gctx = static_cast<GlfwContext&>(ctx);
|
||||
if (gctx.window) {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
glfwDestroyWindow(gctx.window);
|
||||
gctx.window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ticksMs(const Context &ctx) noexcept {
|
||||
using namespace std::chrono;
|
||||
auto &gctx = static_cast<const GlfwContext&>(ctx);
|
||||
const auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
return static_cast<uint64_t>(now - gctx.startTime);
|
||||
}
|
||||
|
||||
bool buttonDown(const Context &ctx, Key key) noexcept {
|
||||
auto &gctx = static_cast<const GlfwContext&>(ctx);
|
||||
return (gctx.keysDown >> static_cast<int>(key)) & 1;
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
auto &gctx = static_cast<GlfwContext&>(ctx);
|
||||
glfwSetWindowShouldClose(gctx.window, true);
|
||||
}
|
||||
|
||||
}
|
50
src/turbine/include/turbine/clipboard.hpp
Normal file
50
src/turbine/include/turbine/clipboard.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class BaseClipboardObject {
|
||||
public:
|
||||
virtual ~BaseClipboardObject() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::String typeId() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto typeMatch(auto name, auto version) const noexcept {
|
||||
return typeId() == ox::buildTypeId(name, version);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ClipboardObject: public BaseClipboardObject {
|
||||
[[nodiscard]]
|
||||
ox::String typeId() const noexcept final {
|
||||
return ox::buildTypeId(T::TypeName, T::TypeVersion);
|
||||
}
|
||||
};
|
||||
|
||||
ox::String getClipboardText(Context &ctx) noexcept;
|
||||
|
||||
void setClipboardText(Context &ctx, ox::CRStringView text) noexcept;
|
||||
|
||||
void setClipboardObject(Context &ctx, ox::UniquePtr<BaseClipboardObject> &&obj) noexcept;
|
||||
|
||||
ox::Result<BaseClipboardObject*> getClipboardData(Context &ctx, ox::StringView typeName, int typeVersion) noexcept;
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T*> getClipboardObject(Context &ctx) noexcept {
|
||||
oxRequire(p, getClipboardData(ctx, T::TypeName, T::TypeVersion));
|
||||
return dynamic_cast<T*>(p);
|
||||
}
|
||||
|
||||
}
|
56
src/turbine/include/turbine/context.hpp
Normal file
56
src/turbine/include/turbine/context.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
|
||||
struct ContextDeleter {
|
||||
void operator()(Context *p) noexcept;
|
||||
};
|
||||
|
||||
using ContextUPtr = ox::UPtr<Context, ContextDeleter>;
|
||||
|
||||
void shutdown(Context &ctx) noexcept;
|
||||
|
||||
keel::Context const&keelCtx(Context const&ctx) noexcept;
|
||||
|
||||
keel::Context &keelCtx(Context &ctx) noexcept;
|
||||
|
||||
inline ox::FileSystem const*rom(Context const&ctx) noexcept {
|
||||
return keelCtx(ctx).rom.get();
|
||||
}
|
||||
|
||||
inline ox::FileSystem *rom(Context &ctx) noexcept {
|
||||
return keelCtx(ctx).rom.get();
|
||||
}
|
||||
|
||||
void setApplicationData(Context &ctx, void *applicationData) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
void *applicationDataRaw(Context &ctx) noexcept;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
T *applicationData(Context &ctx) noexcept {
|
||||
return static_cast<T*>(applicationDataRaw(ctx));
|
||||
}
|
||||
|
||||
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
|
||||
|
||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept;
|
||||
|
||||
}
|
||||
|
@@ -41,6 +41,8 @@ ox::Size getScreenSize(Context &ctx) noexcept;
|
||||
|
||||
ox::Bounds getWindowBounds(Context &ctx) noexcept;
|
||||
|
||||
ox::Error setWindowBounds(Context &ctx, const ox::Bounds &bnds) noexcept;
|
||||
ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept;
|
||||
|
||||
void setConstantRefresh(Context &ctx, bool r) noexcept;
|
||||
|
||||
}
|
@@ -72,7 +72,7 @@ enum Key {
|
||||
class Context;
|
||||
|
||||
[[nodiscard]]
|
||||
bool buttonDown(const Context &ctx, Key) noexcept;
|
||||
bool buttonDown(Context const&ctx, Key) noexcept;
|
||||
|
||||
using KeyEventHandler = void(*)(Context&, Key, bool);
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include "clipboard.hpp"
|
||||
@@ -13,16 +14,14 @@
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept;
|
||||
|
||||
void shutdown(Context &ctx) noexcept;
|
||||
ox::Result<ContextUPtr> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept;
|
||||
|
||||
ox::Error run(Context &ctx) noexcept;
|
||||
|
||||
// Returns the number of milliseconds that have passed since the start of the
|
||||
// program.
|
||||
[[nodiscard]]
|
||||
uint64_t ticksMs(const Context &ctx) noexcept;
|
||||
uint64_t ticksMs(Context const&ctx) noexcept;
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept;
|
||||
|
40
src/turbine/src/CMakeLists.txt
Normal file
40
src/turbine/src/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
add_library(Turbine)
|
||||
|
||||
set(TURBINE_BACKEND_GBA ${TURBINE_BUILD_TYPE} STREQUAL "GBA")
|
||||
set(TURBINE_BACKEND_GLFW NOT ${TURBINE_BACKEND_GBA})
|
||||
|
||||
add_subdirectory(gba)
|
||||
if(${TURBINE_BACKEND_GLFW})
|
||||
add_subdirectory(glfw)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
Turbine PUBLIC
|
||||
../include
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Turbine PUBLIC
|
||||
Keel
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
Turbine PRIVATE
|
||||
TURBINE_BACKEND_GBA=$<IF:$<BOOL:${TURBINE_BACKEND_GBA}>,1,0>
|
||||
TURBINE_BACKEND_GLFW=$<IF:$<BOOL:${TURBINE_BACKEND_GLFW}>,1,0>
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY
|
||||
../include/turbine
|
||||
DESTINATION
|
||||
include/turbine
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
Turbine
|
||||
DESTINATION
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
@@ -1,13 +1,24 @@
|
||||
option(TURBINE_GBA_EVENT_LOOP_TIMER_BASED "Run event loop on time instead of vsync" OFF)
|
||||
set(TURBINE_GBA_TIMER_BITS "32" CACHE STRING "Bits for system time (16, 32, 64)")
|
||||
|
||||
add_library(Turbine-GBA OBJECT)
|
||||
target_sources(
|
||||
Turbine-GBA PRIVATE
|
||||
context.cpp
|
||||
clipboard.cpp
|
||||
event.cpp
|
||||
gfx.cpp
|
||||
irq.arm.cpp
|
||||
turbine.arm.cpp
|
||||
turbine.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
Turbine-GBA PRIVATE
|
||||
TURBINE_GBA_EVENT_LOOP_TIMER_BASED=$<IF:$<BOOL:${TURBINE_GBA_EVENT_LOOP_TIMER_BASED}>,true,false>
|
||||
TURBINE_GBA_TIMER_BITS=${TURBINE_GBA_TIMER_BITS}
|
||||
)
|
||||
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
enable_language(ASM)
|
||||
set_source_files_properties(turbine.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||
@@ -23,6 +34,11 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
Turbine-GBA PUBLIC
|
||||
../../include
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Turbine-GBA PUBLIC
|
||||
TeaGBA
|
@@ -13,7 +13,6 @@ ox::String getClipboardText(Context&) noexcept {
|
||||
}
|
||||
|
||||
void setClipboardText(Context&, ox::CRStringView) noexcept {
|
||||
|
||||
}
|
||||
|
||||
}
|
12
src/turbine/src/gba/config.hpp
Normal file
12
src/turbine/src/gba/config.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace turbine::config {
|
||||
|
||||
constexpr bool GbaEventLoopTimerBased = TURBINE_GBA_EVENT_LOOP_TIMER_BASED;
|
||||
constexpr int GbaTimerBits = TURBINE_GBA_TIMER_BITS;
|
||||
|
||||
}
|
29
src/turbine/src/gba/context.cpp
Normal file
29
src/turbine/src/gba/context.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
}
|
||||
|
||||
keel::Context const&keelCtx(Context const&ctx) noexcept {
|
||||
return ctx.keelCtx;
|
||||
}
|
||||
|
||||
keel::Context &keelCtx(Context &ctx) noexcept {
|
||||
return ctx.keelCtx;
|
||||
}
|
||||
|
||||
void setApplicationData(Context &ctx, void *applicationData) noexcept {
|
||||
ctx.applicationData = applicationData;
|
||||
}
|
||||
|
||||
void *applicationDataRaw(Context &ctx) noexcept {
|
||||
return ctx.applicationData;
|
||||
}
|
||||
|
||||
}
|
36
src/turbine/src/gba/context.hpp
Normal file
36
src/turbine/src/gba/context.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <keel/context.hpp>
|
||||
|
||||
#include <turbine/clipboard.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
#include <turbine/event.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
UpdateHandler updateHandler = [](Context&) -> int {return 0;};
|
||||
keel::Context keelCtx;
|
||||
KeyEventHandler keyEventHandler = nullptr;
|
||||
void *applicationData = nullptr;
|
||||
|
||||
// GBA impl data /////////////////////////////////////////////////////////
|
||||
bool running = true;
|
||||
|
||||
Context() noexcept = default;
|
||||
Context(Context &other) noexcept = delete;
|
||||
Context(Context const&other) noexcept = delete;
|
||||
Context(Context const&&other) noexcept = delete;
|
||||
|
||||
virtual inline ~Context() noexcept {
|
||||
shutdown(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@@ -2,8 +2,9 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <turbine/event.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "event.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
@@ -11,4 +12,12 @@ 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;
|
||||
}
|
||||
|
||||
}
|
@@ -43,7 +43,7 @@ ox::Bounds getWindowBounds(Context&) noexcept {
|
||||
return {0, 0, 240, 160};
|
||||
}
|
||||
|
||||
ox::Error setWindowBounds(Context&, const ox::Bounds&) noexcept {
|
||||
ox::Error setWindowBounds(Context&, ox::Bounds const&) noexcept {
|
||||
return OxError(1, "setWindowBounds not supported on GBA");
|
||||
}
|
||||
|
@@ -16,8 +16,7 @@ static gba_timer_t g_wakeupTime{};
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
g_wakeupTime = 0;
|
||||
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
|
||||
while (gbaCtx->running) {
|
||||
while (ctx.running) {
|
||||
if (g_wakeupTime <= g_timerMs && ctx.updateHandler) {
|
||||
auto sleepTime = ctx.updateHandler(ctx);
|
||||
if (sleepTime >= 0) {
|
@@ -6,6 +6,7 @@
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
@@ -46,16 +47,17 @@ static ox::Result<std::size_t> findPreloadSection() noexcept {
|
||||
constexpr auto headerP2Len = ox_strlen(headerP1);
|
||||
constexpr auto headerLen = headerP1Len + headerP2Len;
|
||||
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
||||
if (ox_memcmp(current, headerP1, headerP1Len) == 0 &&
|
||||
ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
||||
if (memcmp(current, headerP1, headerP1Len) == 0 &&
|
||||
memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
||||
return reinterpret_cast<std::size_t>(current + headerLen);
|
||||
}
|
||||
}
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
ox::Result<ox::UniquePtr<turbine::Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
||||
auto ctx = ox::make_unique<gba::Context>();
|
||||
ox::Result<ContextUPtr> init(
|
||||
ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
||||
auto ctx = ox::make_unique<Context>();
|
||||
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||
#ifdef OX_BARE_METAL
|
||||
oxReturnError(findPreloadSection().moveTo(&ctx->keelCtx.preloadSectionOffset));
|
||||
@@ -63,7 +65,7 @@ ox::Result<ox::UniquePtr<turbine::Context>> init(ox::UPtr<ox::FileSystem> fs, ox
|
||||
oxReturnError(initGfx(*ctx));
|
||||
initTimer();
|
||||
initIrq();
|
||||
return ox::UPtr<turbine::Context>(std::move(ctx));
|
||||
return ox::UPtr<turbine::Context, ContextDeleter>(std::move(ctx));
|
||||
}
|
||||
|
||||
void shutdown(Context&) noexcept {
|
||||
@@ -73,13 +75,12 @@ uint64_t ticksMs(Context&) noexcept {
|
||||
return g_timerMs;
|
||||
}
|
||||
|
||||
bool buttonDown(Context&, Key k) noexcept {
|
||||
bool buttonDown(Context const&, Key k) noexcept {
|
||||
return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast<int>(k)));
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
|
||||
gbaCtx->running = false;
|
||||
ctx.running = false;
|
||||
}
|
||||
|
||||
}
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include "../config.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
24
src/turbine/src/glfw/CMakeLists.txt
Normal file
24
src/turbine/src/glfw/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
option(TURBINE_USE_IMGUI "Include DearImGUI in build (GLFW only)" ON)
|
||||
option(TURBINE_GL_FPS_PRINT "Print FPS to stdout" OFF)
|
||||
|
||||
target_sources(
|
||||
Turbine PRIVATE
|
||||
context.cpp
|
||||
clipboard.cpp
|
||||
event.cpp
|
||||
gfx.cpp
|
||||
turbine.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
Turbine PRIVATE
|
||||
TURBINE_USE_IMGUI=$<IF:$<BOOL:${TURBINE_USE_IMGUI}>,1,0>
|
||||
TURBINE_GL_FPS_PRINT=$<IF:$<BOOL:${TURBINE_GL_FPS_PRINT}>,true,false>
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Turbine PUBLIC
|
||||
glad
|
||||
glfw
|
||||
imgui
|
||||
)
|
36
src/turbine/src/glfw/clipboard.cpp
Normal file
36
src/turbine/src/glfw/clipboard.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::String getClipboardText(Context &ctx) noexcept {
|
||||
return ox::String(glfwGetClipboardString(ctx.window));
|
||||
}
|
||||
|
||||
void setClipboardText(Context &ctx, ox::CRStringView text) noexcept {
|
||||
auto cstr = ox_malloca(text.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), text.data(), text.bytes());
|
||||
glfwSetClipboardString(ctx.window, cstr.get());
|
||||
}
|
||||
|
||||
void setClipboardObject(Context &ctx, ox::UniquePtr<BaseClipboardObject> &&obj) noexcept {
|
||||
ctx.clipboard = std::move(obj);
|
||||
}
|
||||
|
||||
ox::Result<BaseClipboardObject*> getClipboardData(Context &ctx, ox::StringView typeName, int typeVersion) noexcept {
|
||||
if (ctx.clipboard && ctx.clipboard->typeMatch(typeName, typeVersion)) {
|
||||
return ctx.clipboard.get();
|
||||
}
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
}
|
11
src/turbine/src/glfw/config.hpp
Normal file
11
src/turbine/src/glfw/config.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace turbine::config {
|
||||
|
||||
constexpr bool GlFpsPrint = TURBINE_GL_FPS_PRINT;
|
||||
|
||||
}
|
31
src/turbine/src/glfw/context.cpp
Normal file
31
src/turbine/src/glfw/context.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
}
|
||||
|
||||
keel::Context const&keelCtx(Context const&ctx) noexcept {
|
||||
return ctx.keelCtx;
|
||||
}
|
||||
|
||||
keel::Context &keelCtx(Context &ctx) noexcept {
|
||||
return ctx.keelCtx;
|
||||
}
|
||||
|
||||
void setApplicationData(Context &ctx, void *applicationData) noexcept {
|
||||
ctx.applicationData = applicationData;
|
||||
}
|
||||
|
||||
void *applicationDataRaw(Context &ctx) noexcept {
|
||||
return ctx.applicationData;
|
||||
}
|
||||
|
||||
}
|
41
src/turbine/src/glfw/context.hpp
Normal file
41
src/turbine/src/glfw/context.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <turbine/clipboard.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
#include <turbine/event.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
UpdateHandler updateHandler = [](Context&) -> int {return 0;};
|
||||
keel::Context keelCtx;
|
||||
KeyEventHandler keyEventHandler = nullptr;
|
||||
void *applicationData = nullptr;
|
||||
|
||||
// GLFW impl data ////////////////////////////////////////////////////////
|
||||
int uninterruptedRefreshes = 3;
|
||||
ox::UPtr<BaseClipboardObject> clipboard;
|
||||
struct GLFWwindow *window = nullptr;
|
||||
// sets screen refresh to constant instead of only on event
|
||||
bool constantRefresh = true;
|
||||
ox::Vector<gl::Drawer*, 5> drawers;
|
||||
int64_t startTime = 0;
|
||||
uint64_t wakeupTime = 0;
|
||||
uint64_t keysDown = 0;
|
||||
uint64_t prevFpsCheckTime = 0;
|
||||
uint64_t draws = 0;
|
||||
|
||||
Context() noexcept = default;
|
||||
|
||||
Context(Context const&other) noexcept = delete;
|
||||
Context(Context const&&other) noexcept = delete;
|
||||
|
||||
};
|
||||
|
||||
}
|
23
src/turbine/src/glfw/event.cpp
Normal file
23
src/turbine/src/glfw/event.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 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;
|
||||
}
|
||||
|
||||
}
|
@@ -4,38 +4,27 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#if TURBINE_USE_IMGUI
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#endif
|
||||
|
||||
#include <ox/std/defines.hpp>
|
||||
|
||||
#include <turbine/config.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
[[nodiscard]]
|
||||
inline GlfwContext &glctx(Context &ctx) noexcept {
|
||||
if constexpr(ox::defines::Debug) {
|
||||
return dynamic_cast<GlfwContext&>(ctx);
|
||||
} else {
|
||||
return static_cast<GlfwContext&>(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
namespace gl {
|
||||
|
||||
void addDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
gctx.drawers.emplace_back(cd);
|
||||
ctx.drawers.emplace_back(cd);
|
||||
}
|
||||
|
||||
void removeDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
for (auto i = 0u; i < gctx.drawers.size(); ++i) {
|
||||
if (gctx.drawers[i] == cd) {
|
||||
oxIgnoreError(gctx.drawers.erase(i));
|
||||
for (auto i = 0u; i < ctx.drawers.size(); ++i) {
|
||||
if (ctx.drawers[i] == cd) {
|
||||
oxIgnoreError(ctx.drawers.erase(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -47,15 +36,12 @@ static void handleGlfwError(int err, const char *desc) noexcept {
|
||||
oxErrf("GLFW error ({}): {}\n", err, desc);
|
||||
}
|
||||
|
||||
static auto setKeyDownStatus(GlfwContext *gctx, Key key, bool down) noexcept {
|
||||
if (down) {
|
||||
gctx->keysDown |= 1llu << static_cast<int>(key);
|
||||
} else {
|
||||
gctx->keysDown &= ~(1llu << static_cast<int>(key));
|
||||
}
|
||||
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 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) {
|
||||
@@ -73,12 +59,11 @@ static void handleKeyPress(Context *ctx, int key, bool down) noexcept {
|
||||
map[GLFW_KEY_ESCAPE] = Key::Escape;
|
||||
return map;
|
||||
}();
|
||||
const auto eventHandler = keyEventHandler(*ctx);
|
||||
auto &gctx = glctx(*ctx);
|
||||
const auto eventHandler = keyEventHandler(ctx);
|
||||
const auto k = keyMap[static_cast<std::size_t>(key)];
|
||||
setKeyDownStatus(&gctx, k, down);
|
||||
setKeyDownStatus(ctx, k, down);
|
||||
if (eventHandler) {
|
||||
eventHandler(*ctx, k, down);
|
||||
eventHandler(ctx, k, down);
|
||||
}
|
||||
//if constexpr(ox::defines::Debug) {
|
||||
// switch (key) {
|
||||
@@ -96,20 +81,21 @@ static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
||||
}
|
||||
|
||||
static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept {
|
||||
const auto ctx = static_cast<GlfwContext*>(glfwGetWindowUserPointer(window));
|
||||
const auto ctx = static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx->uninterruptedRefreshes = 25;
|
||||
}
|
||||
|
||||
static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept {
|
||||
const auto ctx = static_cast<GlfwContext*>(glfwGetWindowUserPointer(window));
|
||||
const auto ctx = static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx->uninterruptedRefreshes = 25;
|
||||
if (action == GLFW_PRESS) {
|
||||
handleKeyPress(ctx, key, true);
|
||||
handleKeyPress(*ctx, key, true);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
handleKeyPress(ctx, key, false);
|
||||
handleKeyPress(*ctx, key, false);
|
||||
}
|
||||
}
|
||||
|
||||
#if TURBINE_USE_IMGUI
|
||||
static void themeImgui() noexcept {
|
||||
// Dark Ruda style by Raikiri from ImThemes
|
||||
auto &style = ImGui::GetStyle();
|
||||
@@ -207,9 +193,9 @@ static void themeImgui() noexcept {
|
||||
style.Colors[ImGuiCol_NavWindowingDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.2000000029802322);
|
||||
style.Colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355);
|
||||
}
|
||||
#endif
|
||||
|
||||
ox::Error initGfx(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
glfwSetErrorCallback(handleGlfwError);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
@@ -218,86 +204,76 @@ ox::Error initGfx(Context &ctx) noexcept {
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||
}
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
auto cstr = ox_malloca(ctx.keelCtx.appName.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), ctx.keelCtx.appName.data(), ctx.keelCtx.appName.bytes());
|
||||
constexpr auto Scale = 5;
|
||||
gctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr.get(), nullptr, nullptr);
|
||||
//gctx.window = glfwCreateWindow(868, 741, cstr.get(), nullptr, nullptr);
|
||||
if (gctx.window == nullptr) {
|
||||
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 OxError(1, "Could not open GLFW window");
|
||||
}
|
||||
glfwSetCursorPosCallback(gctx.window, handleGlfwCursorPosEvent);
|
||||
glfwSetMouseButtonCallback(gctx.window, handleGlfwMouseButtonEvent);
|
||||
glfwSetKeyCallback(gctx.window, handleGlfwKeyEvent);
|
||||
glfwSetWindowUserPointer(gctx.window, &ctx);
|
||||
glfwMakeContextCurrent(gctx.window);
|
||||
glfwSetCursorPosCallback(ctx.window, handleGlfwCursorPosEvent);
|
||||
glfwSetMouseButtonCallback(ctx.window, handleGlfwMouseButtonEvent);
|
||||
glfwSetKeyCallback(ctx.window, handleGlfwKeyEvent);
|
||||
glfwSetWindowUserPointer(ctx.window, &ctx);
|
||||
glfwMakeContextCurrent(ctx.window);
|
||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
|
||||
return OxError(2, "Could not init Glad");
|
||||
}
|
||||
if constexpr(config::ImGuiEnabled) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
auto &io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
//io.MouseDrawCursor = true;
|
||||
ImGui_ImplGlfw_InitForOpenGL(gctx.window, true);
|
||||
ImGui_ImplOpenGL3_Init();
|
||||
themeImgui();
|
||||
}
|
||||
#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();
|
||||
themeImgui();
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
auto cstr = ox_malloca(title.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), title.data(), title.bytes());
|
||||
glfwSetWindowTitle(gctx.window, cstr.get());
|
||||
glfwSetWindowTitle(ctx.window, cstr.get());
|
||||
}
|
||||
|
||||
void focusWindow(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
glfwFocusWindow(gctx.window);
|
||||
glfwFocusWindow(ctx.window);
|
||||
}
|
||||
|
||||
int getScreenWidth(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
int getScreenHeight(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
ox::Size getScreenSize(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return {w, h};
|
||||
}
|
||||
|
||||
ox::Bounds getWindowBounds(Context &ctx) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
ox::Bounds bnds;
|
||||
glfwGetWindowPos(gctx.window, &bnds.x, &bnds.y);
|
||||
glfwGetWindowSize(gctx.window, &bnds.width, &bnds.height);
|
||||
glfwGetWindowPos(ctx.window, &bnds.x, &bnds.y);
|
||||
glfwGetWindowSize(ctx.window, &bnds.width, &bnds.height);
|
||||
return bnds;
|
||||
}
|
||||
|
||||
ox::Error setWindowBounds(Context &ctx, const ox::Bounds &bnds) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
glfwSetWindowPos(gctx.window, bnds.x, bnds.y);
|
||||
glfwSetWindowSize(gctx.window, bnds.width, bnds.height);
|
||||
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 setConstantRefresh(Context &ctx, bool r) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
gctx.constantRefresh = r;
|
||||
ctx.constantRefresh = r;
|
||||
}
|
||||
|
||||
}
|
123
src/turbine/src/glfw/turbine.cpp
Normal file
123
src/turbine/src/glfw/turbine.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#if TURBINE_USE_IMGUI
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#endif
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
static void draw(Context &ctx) noexcept {
|
||||
// draw start
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
#endif
|
||||
for (auto d : ctx.drawers) {
|
||||
d->draw(ctx);
|
||||
}
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
#endif
|
||||
// draw end
|
||||
glfwSwapBuffers(ctx.window);
|
||||
}
|
||||
|
||||
static void draw(GLFWwindow *window, int, int) noexcept {
|
||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
draw(ctx);
|
||||
}
|
||||
|
||||
ox::Result<ContextUPtr> init(
|
||||
ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
||||
auto ctx = ox::make_unique<Context>();
|
||||
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||
using namespace std::chrono;
|
||||
ctx->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
glfwInit();
|
||||
oxReturnError(initGfx(*ctx));
|
||||
glfwSetWindowSizeCallback(ctx->window, draw);
|
||||
return ox::UPtr<Context, ContextDeleter>(ctx.release());
|
||||
}
|
||||
|
||||
static void tickFps(Context &ctx, uint64_t nowMs) noexcept {
|
||||
++ctx.draws;
|
||||
if (ctx.draws >= 500) {
|
||||
const auto duration = static_cast<double>(nowMs - ctx.prevFpsCheckTime) / 1000.0;
|
||||
const auto fps = static_cast<int>(static_cast<double>(ctx.draws) / duration);
|
||||
if constexpr(config::GlFpsPrint) {
|
||||
oxOutf("FPS: {}\n", fps);
|
||||
}
|
||||
oxTracef("turbine.fps", "FPS: {}", fps);
|
||||
ctx.prevFpsCheckTime = nowMs;
|
||||
ctx.draws = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
int sleepTime = 0;
|
||||
while (!glfwWindowShouldClose(ctx.window)) {
|
||||
glfwPollEvents();
|
||||
const auto ticks = ticksMs(ctx);
|
||||
if (ctx.wakeupTime <= ticks) {
|
||||
sleepTime = ctx.updateHandler(ctx);
|
||||
if (sleepTime >= 0) {
|
||||
ctx.wakeupTime = ticks + static_cast<unsigned>(sleepTime);
|
||||
} else {
|
||||
ctx.wakeupTime = ~uint64_t(0);
|
||||
}
|
||||
} else {
|
||||
sleepTime = 10;
|
||||
}
|
||||
tickFps(ctx, ticks);
|
||||
draw(ctx);
|
||||
if (!ctx.constantRefresh) {
|
||||
if (ctx.uninterruptedRefreshes) {
|
||||
--ctx.uninterruptedRefreshes;
|
||||
} else {
|
||||
glfwWaitEventsTimeout(sleepTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
shutdown(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
void shutdown(Context &ctx) noexcept {
|
||||
if (ctx.window) {
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
#endif
|
||||
glfwDestroyWindow(ctx.window);
|
||||
ctx.window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ticksMs(Context const&ctx) noexcept {
|
||||
using namespace std::chrono;
|
||||
const auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
return static_cast<uint64_t>(now - ctx.startTime);
|
||||
}
|
||||
|
||||
bool buttonDown(Context const&ctx, Key key) noexcept {
|
||||
return (ctx.keysDown >> static_cast<int>(key)) & 1;
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
glfwSetWindowShouldClose(ctx.window, true);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user