[keel,nostalgia,studio,turbine] Cleanup structure of Turbine
This commit is contained in:
parent
1298051a1a
commit
facde6bdce
@ -134,7 +134,7 @@ ox::Error preloadDir(
|
||||
}
|
||||
|
||||
template<typename PlatSpec>
|
||||
ox::Error appendBinary(ox::Buffer &binBuff, ox::Buffer &fsBuff, ox::Preloader<PlatSpec> &pl) noexcept {
|
||||
ox::Error appendBinary(ox::Buffer &binBuff, ox::SpanView<char> const&fsBuff, ox::Preloader<PlatSpec> &pl) noexcept {
|
||||
constexpr auto padbin = [](ox::BufferWriter &w, unsigned factor) noexcept -> ox::Error {
|
||||
return w.write(nullptr, factor - w.buff().size() % factor);
|
||||
};
|
||||
|
@ -9,15 +9,14 @@
|
||||
|
||||
namespace keel {
|
||||
|
||||
#ifndef OX_BARE_METAL
|
||||
[[nodiscard]]
|
||||
static ox::Result<const BaseConverter*> findConverter(
|
||||
keel::Context &ctx,
|
||||
ox::Vector<const BaseConverter*> const&converters,
|
||||
ox::CRStringView srcTypeName,
|
||||
int srcTypeVersion,
|
||||
ox::CRStringView dstTypeName,
|
||||
int dstTypeVersion) noexcept {
|
||||
for (auto &c : ctx.converters) {
|
||||
for (auto &c : converters) {
|
||||
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
||||
return c;
|
||||
}
|
||||
@ -27,23 +26,24 @@ static ox::Result<const BaseConverter*> findConverter(
|
||||
|
||||
static ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
[[maybe_unused]] keel::Context &ctx,
|
||||
ox::Vector<const BaseConverter*> const&converters,
|
||||
[[maybe_unused]] const ox::Buffer &srcBuffer,
|
||||
[[maybe_unused]] ox::CRStringView srcTypeName,
|
||||
[[maybe_unused]] int srcTypeVersion,
|
||||
[[maybe_unused]] ox::CRStringView dstTypeName,
|
||||
[[maybe_unused]] int dstTypeVersion) noexcept {
|
||||
// look for direct converter
|
||||
auto [c, err] = findConverter(ctx, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||
auto [c, err] = findConverter(converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||
if (!err) {
|
||||
return c->convertBuffToPtr(ctx, srcBuffer);
|
||||
}
|
||||
// try to chain multiple converters
|
||||
for (const auto &subConverter : ctx.converters) {
|
||||
for (const auto &subConverter : converters) {
|
||||
if (!subConverter->dstMatches(dstTypeName, dstTypeVersion)) {
|
||||
continue;
|
||||
}
|
||||
const auto [intermediate, chainErr] =
|
||||
convert(ctx, srcBuffer, srcTypeName, srcTypeVersion,
|
||||
convert(ctx, converters, srcBuffer, srcTypeName, srcTypeVersion,
|
||||
subConverter->srcTypeName(), subConverter->srcTypeVersion());
|
||||
if (!chainErr) {
|
||||
return subConverter->convertPtrToPtr(ctx, *intermediate);
|
||||
@ -51,7 +51,6 @@ static ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
}
|
||||
return OxError(1, "Could not convert between types");
|
||||
}
|
||||
#endif
|
||||
|
||||
ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
[[maybe_unused]] keel::Context &ctx,
|
||||
@ -60,7 +59,14 @@ ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
[[maybe_unused]] int dstTypeVersion) noexcept {
|
||||
#ifndef OX_BARE_METAL
|
||||
oxRequire(hdr, readAssetHeader(srcBuffer));
|
||||
return convert(ctx, srcBuffer, hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, dstTypeName, dstTypeVersion);
|
||||
return convert(
|
||||
ctx,
|
||||
ctx.converters,
|
||||
srcBuffer,
|
||||
hdr.clawHdr.typeName,
|
||||
hdr.clawHdr.typeVersion,
|
||||
dstTypeName,
|
||||
dstTypeVersion);
|
||||
#else
|
||||
return OxError(1, "Operation not supported on this platform");
|
||||
#endif
|
||||
|
@ -27,7 +27,7 @@ class Context {
|
||||
|
||||
};
|
||||
|
||||
ox::Result<ox::UPtr<Context>> init(turbine::Context &tctx, InitParams const& = {}) noexcept;
|
||||
ox::Result<ox::UPtr<Context>> init(turbine::Context &tctx, InitParams const¶ms = {}) noexcept;
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,10 @@ target_link_libraries(
|
||||
NostalgiaCore-GBA PUBLIC
|
||||
TeaGBA
|
||||
Keel
|
||||
Turbine
|
||||
)
|
||||
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||
target_link_libraries(NostalgiaCore PUBLIC NostalgiaCore-GBA)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -15,8 +15,8 @@ struct GbaContext: public core::Context {
|
||||
explicit GbaContext(turbine::Context &tctx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
auto const&rom() const noexcept {
|
||||
return *turbine::rom(turbineCtx);
|
||||
ox::MemFS const&rom() const noexcept {
|
||||
return static_cast<ox::MemFS const&>(*turbine::rom(turbineCtx));
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -142,7 +142,7 @@ ox::Error loadBgTileSheet(
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
||||
auto &rom = gctx.rom();
|
||||
return loadBgTileSheet(rom, cbb, tilesheetAddr, paletteAddr);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ ox::Error loadSpriteTileSheet(
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
||||
auto &rom = gctx.rom();
|
||||
oxRequire(tsStat, gctx.rom().stat(tilesheetAddr));
|
||||
oxRequire(ts, rom.directAccess(tilesheetAddr));
|
||||
GbaTileMapTarget target;
|
||||
@ -169,7 +169,7 @@ ox::Error loadSpriteTileSheet(
|
||||
|
||||
ox::Error loadBgPalette(Context &ctx, unsigned, ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
||||
auto &rom = gctx.rom();
|
||||
GbaPaletteTarget target;
|
||||
target.palette = MEM_BG_PALETTE;
|
||||
oxRequire(palStat, gctx.rom().stat(paletteAddr));
|
||||
@ -180,7 +180,7 @@ ox::Error loadBgPalette(Context &ctx, unsigned, ox::FileAddress const&paletteAdd
|
||||
|
||||
ox::Error loadSpritePalette(Context &ctx, unsigned cbb, ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
||||
auto &rom = gctx.rom();
|
||||
GbaPaletteTarget target;
|
||||
target.palette = &MEM_SPRITE_PALETTE[cbb];
|
||||
oxRequire(palStat, rom.stat(paletteAddr));
|
||||
|
@ -43,7 +43,7 @@ ox::Array<char, 128> charMap = {
|
||||
0, // space
|
||||
38, // !
|
||||
0, // "
|
||||
0, // #
|
||||
49, // #
|
||||
0, // $
|
||||
0, // %
|
||||
0, // &
|
||||
@ -132,9 +132,9 @@ ox::Array<char, 128> charMap = {
|
||||
25, // y
|
||||
26, // z
|
||||
46, // {
|
||||
0, // |
|
||||
51, // |
|
||||
48, // }
|
||||
0, // ~
|
||||
50, // ~
|
||||
};
|
||||
|
||||
void setSprite(Context &c, Sprite const&s) noexcept {
|
||||
|
@ -435,7 +435,7 @@ ox::Error loadBgTileSheet(
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
auto &kctx = gctx.turbineCtx.keelCtx;
|
||||
auto &kctx = keelCtx(gctx.turbineCtx);
|
||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet).to([](TileSheetData const&t) -> TileSheetData {
|
||||
@ -455,7 +455,7 @@ ox::Error loadSpriteTileSheet(
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &gctx = glctx(ctx);
|
||||
auto &kctx = gctx.turbineCtx.keelCtx;
|
||||
auto &kctx = keelCtx(gctx.turbineCtx);
|
||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
|
||||
|
@ -19,7 +19,7 @@ constexpr uint64_t TileColumns = 128;
|
||||
constexpr uint64_t TileCount = TileRows * TileColumns;
|
||||
constexpr uint64_t SpriteCount = 128;
|
||||
constexpr uint64_t BgVertexVboRows = 4;
|
||||
constexpr uint64_t BgVertexVboRowLength = 4;
|
||||
constexpr uint64_t BgVertexVboRowLength = 5;
|
||||
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
|
||||
constexpr uint64_t BgVertexEboLength = 6;
|
||||
constexpr uint64_t SpriteVertexVboRows = 256;
|
||||
|
@ -17,9 +17,9 @@ namespace nostalgia::core {
|
||||
|
||||
PaletteEditorImGui::PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path):
|
||||
m_ctx(ctx),
|
||||
m_itemPath(std::move(path)),
|
||||
m_itemPath(path),
|
||||
m_itemName(m_itemPath.substr(std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset() + 1)),
|
||||
m_pal(*keel::readObj<Palette>(m_ctx.keelCtx, ox::FileAddress(m_itemPath.c_str())).unwrapThrow()) {
|
||||
m_pal(*keel::readObj<Palette>(keelCtx(m_ctx), ox::FileAddress(m_itemPath.c_str())).unwrapThrow()) {
|
||||
}
|
||||
|
||||
ox::String const&PaletteEditorImGui::itemName() const noexcept {
|
||||
|
@ -567,10 +567,10 @@ class PaletteChangeCommand: public TileSheetCommand {
|
||||
TileSheetEditorModel::TileSheetEditorModel(turbine::Context &ctx, ox::StringView path):
|
||||
m_ctx(ctx),
|
||||
m_path(path) {
|
||||
oxRequireT(img, readObj<TileSheet>(m_ctx.keelCtx, m_path));
|
||||
oxRequireT(img, readObj<TileSheet>(keelCtx(m_ctx), m_path));
|
||||
m_img = *img;
|
||||
if (m_img.defaultPalette) {
|
||||
oxThrowError(readObj<Palette>(m_ctx.keelCtx, m_img.defaultPalette).moveTo(&m_pal));
|
||||
oxThrowError(readObj<Palette>(keelCtx(m_ctx), m_img.defaultPalette).moveTo(&m_pal));
|
||||
}
|
||||
m_pal.updated.connect(this, &TileSheetEditorModel::markUpdated);
|
||||
m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId);
|
||||
@ -634,7 +634,7 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||
if (ox::beginsWith(path, uuidPrefix)) {
|
||||
auto uuid = ox::StringView(path.data() + uuidPrefix.bytes(), path.bytes() - uuidPrefix.bytes());
|
||||
auto out = m_ctx.keelCtx.uuidToPath.at(uuid);
|
||||
auto out = keelCtx(m_ctx).uuidToPath.at(uuid);
|
||||
if (out.error) {
|
||||
return {};
|
||||
}
|
||||
@ -645,7 +645,7 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::String const&path) noexcept {
|
||||
oxRequire(uuid, m_ctx.keelCtx.pathToUuid.at(path));
|
||||
oxRequire(uuid, keelCtx(m_ctx).pathToUuid.at(path));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
@ -753,7 +753,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd)
|
||||
m_updated = true;
|
||||
const auto cmdId = cmd->commandId();
|
||||
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
|
||||
oxReturnError(readObj<Palette>(m_ctx.keelCtx, ox::StringView(m_img.defaultPalette.getPath().value)).moveTo(&m_pal));
|
||||
oxReturnError(readObj<Palette>(keelCtx(m_ctx), ox::StringView(m_img.defaultPalette.getPath().value)).moveTo(&m_pal));
|
||||
}
|
||||
auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd);
|
||||
auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx());
|
||||
|
@ -12,7 +12,7 @@ namespace nostalgia::scene {
|
||||
|
||||
class Scene {
|
||||
private:
|
||||
const SceneStatic &m_sceneStatic;
|
||||
SceneStatic const&m_sceneStatic;
|
||||
|
||||
public:
|
||||
explicit Scene(SceneStatic const&sceneStatic) noexcept;
|
||||
|
@ -37,7 +37,7 @@ struct TileDoc {
|
||||
ox::Array<uint8_t, 4> layerAttachments;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Result<core::SubSheetId> getSubsheetId(const core::TileSheet &ts) const noexcept {
|
||||
constexpr ox::Result<core::SubSheetId> getSubsheetId(core::TileSheet const&ts) const noexcept {
|
||||
// prefer the already present ID
|
||||
if (subsheetId > -1) {
|
||||
return subsheetId;
|
||||
@ -47,7 +47,7 @@ struct TileDoc {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Result<ox::StringView> getSubsheetPath(
|
||||
const core::TileSheet &ts) const noexcept {
|
||||
core::TileSheet const&ts) const noexcept {
|
||||
// prefer the already present path
|
||||
if (!subsheetPath.len()) {
|
||||
return ts.getNameFor(subsheetId);
|
||||
@ -88,7 +88,7 @@ struct SceneDoc {
|
||||
auto colCnt = layer[0].size();
|
||||
// find shortest row (they should all be the same, but you know this data
|
||||
// could come from a file)
|
||||
for (const auto &row : layer) {
|
||||
for (auto const&row : layer) {
|
||||
colCnt = ox::min(colCnt, row.size());
|
||||
}
|
||||
return {static_cast<int>(colCnt), rowCnt};
|
||||
|
@ -16,12 +16,12 @@ ox::Error Scene::setupDisplay(core::Context &ctx) const noexcept {
|
||||
if (m_sceneStatic.palettes.empty()) {
|
||||
return OxError(1, "Scene has no palettes");
|
||||
}
|
||||
const auto &palette = m_sceneStatic.palettes[0];
|
||||
auto const&palette = m_sceneStatic.palettes[0];
|
||||
oxReturnError(core::loadBgTileSheet(
|
||||
ctx, 0, m_sceneStatic.tilesheet, palette));
|
||||
// disable all backgrounds
|
||||
core::setBgStatus(ctx, 0);
|
||||
for (auto layerNo = 0u; const auto &layer : m_sceneStatic.tileMapIdx) {
|
||||
for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) {
|
||||
setupLayer(ctx, layer, layerNo);
|
||||
++layerNo;
|
||||
}
|
||||
@ -37,7 +37,7 @@ void Scene::setupLayer(
|
||||
auto x = 0;
|
||||
auto y = 0;
|
||||
const auto width = m_sceneStatic.rows[layerNo];
|
||||
for (const auto &tile : layer) {
|
||||
for (auto const&tile : layer) {
|
||||
const auto tile8 = static_cast<uint8_t>(tile);
|
||||
core::setTile(ctx, layerNo, x, y, tile8);
|
||||
core::setTile(ctx, layerNo, x + 1, y, tile8 + 1);
|
||||
|
@ -50,7 +50,7 @@ void SceneEditorImGui::onActivated() noexcept {
|
||||
ox::Error SceneEditorImGui::saveItem() noexcept {
|
||||
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
oxReturnError(sctx->project->writeObj(m_itemPath, m_editor.scene()));
|
||||
oxReturnError(m_ctx.keelCtx.assetManager.setAsset(m_itemPath, m_editor.scene()));
|
||||
oxReturnError(keelCtx(m_ctx).assetManager.setAsset(m_itemPath, m_editor.scene()));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace nostalgia::scene {
|
||||
|
||||
SceneEditor::SceneEditor(turbine::Context &ctx, ox::CRStringView path):
|
||||
m_ctx(ctx),
|
||||
m_scene(*keel::readObj<SceneStatic>(m_ctx.keelCtx, path).unwrapThrow()) {
|
||||
m_scene(*keel::readObj<SceneStatic>(keelCtx(m_ctx), path).unwrapThrow()) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class StudioModule: public studio::Module {
|
||||
}
|
||||
};
|
||||
|
||||
static StudioModule mod;
|
||||
static StudioModule const mod;
|
||||
const studio::Module *studioModule() noexcept {
|
||||
return &mod;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
|
||||
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
||||
oxRequireM(cctx, core::init(*tctx));
|
||||
constexpr ox::FileAddress SceneAddr = ox::StringLiteral("/Scenes/Chester.nscn");
|
||||
oxRequire(scn, keel::readObj<scene::SceneStatic>(tctx->keelCtx, SceneAddr));
|
||||
oxRequire(scn, keel::readObj<scene::SceneStatic>(keelCtx(*tctx), SceneAddr));
|
||||
turbine::setUpdateHandler(*tctx, updateHandler);
|
||||
turbine::setKeyEventHandler(*tctx, keyEventHandler);
|
||||
s_scene.emplace(*scn);
|
||||
|
@ -10,7 +10,7 @@
|
||||
namespace studio {
|
||||
|
||||
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept {
|
||||
m_text = ox::sfmt("{} - dev build", ctx.keelCtx.appName);
|
||||
m_text = ox::sfmt("{} - dev build", keelCtx(ctx).appName);
|
||||
}
|
||||
|
||||
void AboutPopup::open() noexcept {
|
||||
|
@ -47,7 +47,7 @@ static ox::Error runApp(
|
||||
ox::CRStringView projectDataDir,
|
||||
ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
||||
oxRequireM(ctx, turbine::init(std::move(fs), appName));
|
||||
turbine::setWindowTitle(*ctx, ctx->keelCtx.appName);
|
||||
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
||||
turbine::setUpdateHandler(*ctx, updateHandler);
|
||||
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
||||
turbine::setConstantRefresh(*ctx, false);
|
||||
|
@ -26,7 +26,7 @@ class ProjectExplorer: public studio::Widget {
|
||||
ox::Error refreshProjectTreeModel(ox::CRStringView = {}) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::FileSystem *romFs() noexcept {
|
||||
inline ox::FileSystem *romFs() noexcept {
|
||||
return rom(*m_ctx);
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ StudioUI::StudioUI(turbine::Context *ctx, ox::StringView projectDir) noexcept:
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
loadModules();
|
||||
// open project and files
|
||||
const auto [config, err] = studio::readConfig<StudioConfig>(&ctx->keelCtx);
|
||||
const auto [config, err] = studio::readConfig<StudioConfig>(&keelCtx(*ctx));
|
||||
m_showProjectExplorer = config.showProjectExplorer;
|
||||
if (!err) {
|
||||
oxIgnoreError(openProject(config.projectPath));
|
||||
@ -223,7 +223,7 @@ void StudioUI::drawTabs() noexcept {
|
||||
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
||||
if (m_activeEditor != e.get()) {
|
||||
m_activeEditor = e.get();
|
||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
||||
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||
config->activeTabItemName = m_activeEditor->itemName();
|
||||
});
|
||||
}
|
||||
@ -276,7 +276,7 @@ void StudioUI::loadModules() noexcept {
|
||||
|
||||
void StudioUI::toggleProjectExplorer() noexcept {
|
||||
m_showProjectExplorer = !m_showProjectExplorer;
|
||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
||||
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||
config->showProjectExplorer = m_showProjectExplorer;
|
||||
});
|
||||
}
|
||||
@ -303,16 +303,16 @@ void StudioUI::save() noexcept {
|
||||
|
||||
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
||||
oxRequireM(fs, keel::loadRomFs(path));
|
||||
oxReturnError(keel::setRomFs(m_ctx->keelCtx, std::move(fs)));
|
||||
turbine::setWindowTitle(*m_ctx, ox::sfmt("{} - {}", m_ctx->keelCtx.appName, path));
|
||||
m_project = ox::make_unique<studio::Project>(m_ctx->keelCtx, ox::String(path), m_projectDir);
|
||||
oxReturnError(keel::setRomFs(keelCtx(*m_ctx), std::move(fs)));
|
||||
turbine::setWindowTitle(*m_ctx, ox::sfmt("{} - {}", keelCtx(*m_ctx).appName, path));
|
||||
m_project = ox::make_unique<studio::Project>(keelCtx(*m_ctx), ox::String(path), m_projectDir);
|
||||
auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||
sctx->project = m_project.get();
|
||||
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_project->fileDeleted.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_openFiles.clear();
|
||||
m_editors.clear();
|
||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
||||
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||
config->projectPath = ox::String(path);
|
||||
config->openFiles.clear();
|
||||
});
|
||||
@ -362,7 +362,7 @@ ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab)
|
||||
m_activeEditorUpdatePending = editor;
|
||||
}
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
||||
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||
if (!config->openFiles.contains(path)) {
|
||||
config->openFiles.emplace_back(path);
|
||||
}
|
||||
@ -376,7 +376,7 @@ ox::Error StudioUI::closeFile(ox::CRStringView path) noexcept {
|
||||
}
|
||||
oxIgnoreError(m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)));
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
||||
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||
oxIgnoreError(config->openFiles.erase(std::remove(config->openFiles.begin(), config->openFiles.end(), path)));
|
||||
});
|
||||
return OxError(0);
|
||||
|
@ -33,7 +33,7 @@ class ItemMakerT: public ItemMaker {
|
||||
const T item;
|
||||
const ox::ClawFormat fmt;
|
||||
public:
|
||||
constexpr explicit ItemMakerT(
|
||||
constexpr ItemMakerT(
|
||||
ox::StringView pDisplayName,
|
||||
ox::StringView pParentDir,
|
||||
ox::StringView fileExt,
|
||||
@ -64,7 +64,7 @@ class ItemMakerT: public ItemMaker {
|
||||
ox::Error write(turbine::Context *ctx, ox::CRStringView pName) const noexcept override {
|
||||
const auto path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(*ctx);
|
||||
keel::createUuidMapping(ctx->keelCtx, path, ox::UUID::generate().unwrap());
|
||||
keel::createUuidMapping(keelCtx(*ctx), path, ox::UUID::generate().unwrap());
|
||||
return sctx->project->writeObj(path, item, fmt);
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user