[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>
|
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 {
|
constexpr auto padbin = [](ox::BufferWriter &w, unsigned factor) noexcept -> ox::Error {
|
||||||
return w.write(nullptr, factor - w.buff().size() % factor);
|
return w.write(nullptr, factor - w.buff().size() % factor);
|
||||||
};
|
};
|
||||||
|
@ -9,15 +9,14 @@
|
|||||||
|
|
||||||
namespace keel {
|
namespace keel {
|
||||||
|
|
||||||
#ifndef OX_BARE_METAL
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static ox::Result<const BaseConverter*> findConverter(
|
static ox::Result<const BaseConverter*> findConverter(
|
||||||
keel::Context &ctx,
|
ox::Vector<const BaseConverter*> const&converters,
|
||||||
ox::CRStringView srcTypeName,
|
ox::CRStringView srcTypeName,
|
||||||
int srcTypeVersion,
|
int srcTypeVersion,
|
||||||
ox::CRStringView dstTypeName,
|
ox::CRStringView dstTypeName,
|
||||||
int dstTypeVersion) noexcept {
|
int dstTypeVersion) noexcept {
|
||||||
for (auto &c : ctx.converters) {
|
for (auto &c : converters) {
|
||||||
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -27,23 +26,24 @@ static ox::Result<const BaseConverter*> findConverter(
|
|||||||
|
|
||||||
static ox::Result<ox::UniquePtr<Wrap>> convert(
|
static ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||||
[[maybe_unused]] keel::Context &ctx,
|
[[maybe_unused]] keel::Context &ctx,
|
||||||
|
ox::Vector<const BaseConverter*> const&converters,
|
||||||
[[maybe_unused]] const ox::Buffer &srcBuffer,
|
[[maybe_unused]] const ox::Buffer &srcBuffer,
|
||||||
[[maybe_unused]] ox::CRStringView srcTypeName,
|
[[maybe_unused]] ox::CRStringView srcTypeName,
|
||||||
[[maybe_unused]] int srcTypeVersion,
|
[[maybe_unused]] int srcTypeVersion,
|
||||||
[[maybe_unused]] ox::CRStringView dstTypeName,
|
[[maybe_unused]] ox::CRStringView dstTypeName,
|
||||||
[[maybe_unused]] int dstTypeVersion) noexcept {
|
[[maybe_unused]] int dstTypeVersion) noexcept {
|
||||||
// look for direct converter
|
// look for direct converter
|
||||||
auto [c, err] = findConverter(ctx, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
auto [c, err] = findConverter(converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return c->convertBuffToPtr(ctx, srcBuffer);
|
return c->convertBuffToPtr(ctx, srcBuffer);
|
||||||
}
|
}
|
||||||
// try to chain multiple converters
|
// try to chain multiple converters
|
||||||
for (const auto &subConverter : ctx.converters) {
|
for (const auto &subConverter : converters) {
|
||||||
if (!subConverter->dstMatches(dstTypeName, dstTypeVersion)) {
|
if (!subConverter->dstMatches(dstTypeName, dstTypeVersion)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto [intermediate, chainErr] =
|
const auto [intermediate, chainErr] =
|
||||||
convert(ctx, srcBuffer, srcTypeName, srcTypeVersion,
|
convert(ctx, converters, srcBuffer, srcTypeName, srcTypeVersion,
|
||||||
subConverter->srcTypeName(), subConverter->srcTypeVersion());
|
subConverter->srcTypeName(), subConverter->srcTypeVersion());
|
||||||
if (!chainErr) {
|
if (!chainErr) {
|
||||||
return subConverter->convertPtrToPtr(ctx, *intermediate);
|
return subConverter->convertPtrToPtr(ctx, *intermediate);
|
||||||
@ -51,7 +51,6 @@ static ox::Result<ox::UniquePtr<Wrap>> convert(
|
|||||||
}
|
}
|
||||||
return OxError(1, "Could not convert between types");
|
return OxError(1, "Could not convert between types");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Wrap>> convert(
|
ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||||
[[maybe_unused]] keel::Context &ctx,
|
[[maybe_unused]] keel::Context &ctx,
|
||||||
@ -60,7 +59,14 @@ ox::Result<ox::UniquePtr<Wrap>> convert(
|
|||||||
[[maybe_unused]] int dstTypeVersion) noexcept {
|
[[maybe_unused]] int dstTypeVersion) noexcept {
|
||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
oxRequire(hdr, readAssetHeader(srcBuffer));
|
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
|
#else
|
||||||
return OxError(1, "Operation not supported on this platform");
|
return OxError(1, "Operation not supported on this platform");
|
||||||
#endif
|
#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
|
NostalgiaCore-GBA PUBLIC
|
||||||
TeaGBA
|
TeaGBA
|
||||||
Keel
|
Keel
|
||||||
|
Turbine
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||||
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
|
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||||
target_link_libraries(NostalgiaCore PUBLIC NostalgiaCore-GBA)
|
target_link_libraries(NostalgiaCore PUBLIC NostalgiaCore-GBA)
|
||||||
endif()
|
endif()
|
||||||
|
@ -15,8 +15,8 @@ struct GbaContext: public core::Context {
|
|||||||
explicit GbaContext(turbine::Context &tctx) noexcept;
|
explicit GbaContext(turbine::Context &tctx) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
auto const&rom() const noexcept {
|
ox::MemFS const&rom() const noexcept {
|
||||||
return *turbine::rom(turbineCtx);
|
return static_cast<ox::MemFS const&>(*turbine::rom(turbineCtx));
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -142,7 +142,7 @@ ox::Error loadBgTileSheet(
|
|||||||
ox::FileAddress const&tilesheetAddr,
|
ox::FileAddress const&tilesheetAddr,
|
||||||
ox::FileAddress const&paletteAddr) noexcept {
|
ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
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);
|
return loadBgTileSheet(rom, cbb, tilesheetAddr, paletteAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ ox::Error loadSpriteTileSheet(
|
|||||||
ox::FileAddress const&tilesheetAddr,
|
ox::FileAddress const&tilesheetAddr,
|
||||||
ox::FileAddress const&paletteAddr) noexcept {
|
ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
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(tsStat, gctx.rom().stat(tilesheetAddr));
|
||||||
oxRequire(ts, rom.directAccess(tilesheetAddr));
|
oxRequire(ts, rom.directAccess(tilesheetAddr));
|
||||||
GbaTileMapTarget target;
|
GbaTileMapTarget target;
|
||||||
@ -169,7 +169,7 @@ ox::Error loadSpriteTileSheet(
|
|||||||
|
|
||||||
ox::Error loadBgPalette(Context &ctx, unsigned, ox::FileAddress const&paletteAddr) noexcept {
|
ox::Error loadBgPalette(Context &ctx, unsigned, ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
auto &rom = gctx.rom();
|
||||||
GbaPaletteTarget target;
|
GbaPaletteTarget target;
|
||||||
target.palette = MEM_BG_PALETTE;
|
target.palette = MEM_BG_PALETTE;
|
||||||
oxRequire(palStat, gctx.rom().stat(paletteAddr));
|
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 {
|
ox::Error loadSpritePalette(Context &ctx, unsigned cbb, ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = static_cast<GbaContext&>(ctx);
|
auto &gctx = static_cast<GbaContext&>(ctx);
|
||||||
auto &rom = static_cast<const ox::MemFS&>(gctx.rom());
|
auto &rom = gctx.rom();
|
||||||
GbaPaletteTarget target;
|
GbaPaletteTarget target;
|
||||||
target.palette = &MEM_SPRITE_PALETTE[cbb];
|
target.palette = &MEM_SPRITE_PALETTE[cbb];
|
||||||
oxRequire(palStat, rom.stat(paletteAddr));
|
oxRequire(palStat, rom.stat(paletteAddr));
|
||||||
|
@ -43,7 +43,7 @@ ox::Array<char, 128> charMap = {
|
|||||||
0, // space
|
0, // space
|
||||||
38, // !
|
38, // !
|
||||||
0, // "
|
0, // "
|
||||||
0, // #
|
49, // #
|
||||||
0, // $
|
0, // $
|
||||||
0, // %
|
0, // %
|
||||||
0, // &
|
0, // &
|
||||||
@ -132,9 +132,9 @@ ox::Array<char, 128> charMap = {
|
|||||||
25, // y
|
25, // y
|
||||||
26, // z
|
26, // z
|
||||||
46, // {
|
46, // {
|
||||||
0, // |
|
51, // |
|
||||||
48, // }
|
48, // }
|
||||||
0, // ~
|
50, // ~
|
||||||
};
|
};
|
||||||
|
|
||||||
void setSprite(Context &c, Sprite const&s) noexcept {
|
void setSprite(Context &c, Sprite const&s) noexcept {
|
||||||
|
@ -435,7 +435,7 @@ ox::Error loadBgTileSheet(
|
|||||||
ox::FileAddress const&tilesheetAddr,
|
ox::FileAddress const&tilesheetAddr,
|
||||||
ox::FileAddress const&paletteAddr) noexcept {
|
ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
auto &gctx = glctx(ctx);
|
||||||
auto &kctx = gctx.turbineCtx.keelCtx;
|
auto &kctx = keelCtx(gctx.turbineCtx);
|
||||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
||||||
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet).to([](TileSheetData const&t) -> TileSheetData {
|
oxRequire(tsd, loadTileSheet(ctx, *tilesheet).to([](TileSheetData const&t) -> TileSheetData {
|
||||||
@ -455,7 +455,7 @@ ox::Error loadSpriteTileSheet(
|
|||||||
ox::FileAddress const&tilesheetAddr,
|
ox::FileAddress const&tilesheetAddr,
|
||||||
ox::FileAddress const&paletteAddr) noexcept {
|
ox::FileAddress const&paletteAddr) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
auto &gctx = glctx(ctx);
|
||||||
auto &kctx = gctx.turbineCtx.keelCtx;
|
auto &kctx = keelCtx(gctx.turbineCtx);
|
||||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
|
||||||
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
|
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
|
||||||
|
@ -19,7 +19,7 @@ constexpr uint64_t TileColumns = 128;
|
|||||||
constexpr uint64_t TileCount = TileRows * TileColumns;
|
constexpr uint64_t TileCount = TileRows * TileColumns;
|
||||||
constexpr uint64_t SpriteCount = 128;
|
constexpr uint64_t SpriteCount = 128;
|
||||||
constexpr uint64_t BgVertexVboRows = 4;
|
constexpr uint64_t BgVertexVboRows = 4;
|
||||||
constexpr uint64_t BgVertexVboRowLength = 4;
|
constexpr uint64_t BgVertexVboRowLength = 5;
|
||||||
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
|
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
|
||||||
constexpr uint64_t BgVertexEboLength = 6;
|
constexpr uint64_t BgVertexEboLength = 6;
|
||||||
constexpr uint64_t SpriteVertexVboRows = 256;
|
constexpr uint64_t SpriteVertexVboRows = 256;
|
||||||
|
@ -17,9 +17,9 @@ namespace nostalgia::core {
|
|||||||
|
|
||||||
PaletteEditorImGui::PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path):
|
PaletteEditorImGui::PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path):
|
||||||
m_ctx(ctx),
|
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_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 {
|
ox::String const&PaletteEditorImGui::itemName() const noexcept {
|
||||||
|
@ -567,10 +567,10 @@ class PaletteChangeCommand: public TileSheetCommand {
|
|||||||
TileSheetEditorModel::TileSheetEditorModel(turbine::Context &ctx, ox::StringView path):
|
TileSheetEditorModel::TileSheetEditorModel(turbine::Context &ctx, ox::StringView path):
|
||||||
m_ctx(ctx),
|
m_ctx(ctx),
|
||||||
m_path(path) {
|
m_path(path) {
|
||||||
oxRequireT(img, readObj<TileSheet>(m_ctx.keelCtx, m_path));
|
oxRequireT(img, readObj<TileSheet>(keelCtx(m_ctx), m_path));
|
||||||
m_img = *img;
|
m_img = *img;
|
||||||
if (m_img.defaultPalette) {
|
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_pal.updated.connect(this, &TileSheetEditorModel::markUpdated);
|
||||||
m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId);
|
m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId);
|
||||||
@ -634,7 +634,7 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
|||||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||||
if (ox::beginsWith(path, uuidPrefix)) {
|
if (ox::beginsWith(path, uuidPrefix)) {
|
||||||
auto uuid = ox::StringView(path.data() + uuidPrefix.bytes(), path.bytes() - uuidPrefix.bytes());
|
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) {
|
if (out.error) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -645,7 +645,7 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error TileSheetEditorModel::setPalette(ox::String const&path) 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()));
|
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -753,7 +753,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd)
|
|||||||
m_updated = true;
|
m_updated = true;
|
||||||
const auto cmdId = cmd->commandId();
|
const auto cmdId = cmd->commandId();
|
||||||
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
|
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 tsCmd = dynamic_cast<const TileSheetCommand*>(cmd);
|
||||||
auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx());
|
auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx());
|
||||||
|
@ -12,7 +12,7 @@ namespace nostalgia::scene {
|
|||||||
|
|
||||||
class Scene {
|
class Scene {
|
||||||
private:
|
private:
|
||||||
const SceneStatic &m_sceneStatic;
|
SceneStatic const&m_sceneStatic;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Scene(SceneStatic const&sceneStatic) noexcept;
|
explicit Scene(SceneStatic const&sceneStatic) noexcept;
|
||||||
|
@ -37,7 +37,7 @@ struct TileDoc {
|
|||||||
ox::Array<uint8_t, 4> layerAttachments;
|
ox::Array<uint8_t, 4> layerAttachments;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[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
|
// prefer the already present ID
|
||||||
if (subsheetId > -1) {
|
if (subsheetId > -1) {
|
||||||
return subsheetId;
|
return subsheetId;
|
||||||
@ -47,7 +47,7 @@ struct TileDoc {
|
|||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr ox::Result<ox::StringView> getSubsheetPath(
|
constexpr ox::Result<ox::StringView> getSubsheetPath(
|
||||||
const core::TileSheet &ts) const noexcept {
|
core::TileSheet const&ts) const noexcept {
|
||||||
// prefer the already present path
|
// prefer the already present path
|
||||||
if (!subsheetPath.len()) {
|
if (!subsheetPath.len()) {
|
||||||
return ts.getNameFor(subsheetId);
|
return ts.getNameFor(subsheetId);
|
||||||
@ -88,7 +88,7 @@ struct SceneDoc {
|
|||||||
auto colCnt = layer[0].size();
|
auto colCnt = layer[0].size();
|
||||||
// find shortest row (they should all be the same, but you know this data
|
// find shortest row (they should all be the same, but you know this data
|
||||||
// could come from a file)
|
// could come from a file)
|
||||||
for (const auto &row : layer) {
|
for (auto const&row : layer) {
|
||||||
colCnt = ox::min(colCnt, row.size());
|
colCnt = ox::min(colCnt, row.size());
|
||||||
}
|
}
|
||||||
return {static_cast<int>(colCnt), rowCnt};
|
return {static_cast<int>(colCnt), rowCnt};
|
||||||
|
@ -16,12 +16,12 @@ ox::Error Scene::setupDisplay(core::Context &ctx) const noexcept {
|
|||||||
if (m_sceneStatic.palettes.empty()) {
|
if (m_sceneStatic.palettes.empty()) {
|
||||||
return OxError(1, "Scene has no palettes");
|
return OxError(1, "Scene has no palettes");
|
||||||
}
|
}
|
||||||
const auto &palette = m_sceneStatic.palettes[0];
|
auto const&palette = m_sceneStatic.palettes[0];
|
||||||
oxReturnError(core::loadBgTileSheet(
|
oxReturnError(core::loadBgTileSheet(
|
||||||
ctx, 0, m_sceneStatic.tilesheet, palette));
|
ctx, 0, m_sceneStatic.tilesheet, palette));
|
||||||
// disable all backgrounds
|
// disable all backgrounds
|
||||||
core::setBgStatus(ctx, 0);
|
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);
|
setupLayer(ctx, layer, layerNo);
|
||||||
++layerNo;
|
++layerNo;
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ void Scene::setupLayer(
|
|||||||
auto x = 0;
|
auto x = 0;
|
||||||
auto y = 0;
|
auto y = 0;
|
||||||
const auto width = m_sceneStatic.rows[layerNo];
|
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);
|
const auto tile8 = static_cast<uint8_t>(tile);
|
||||||
core::setTile(ctx, layerNo, x, y, tile8);
|
core::setTile(ctx, layerNo, x, y, tile8);
|
||||||
core::setTile(ctx, layerNo, x + 1, y, tile8 + 1);
|
core::setTile(ctx, layerNo, x + 1, y, tile8 + 1);
|
||||||
|
@ -50,7 +50,7 @@ void SceneEditorImGui::onActivated() noexcept {
|
|||||||
ox::Error SceneEditorImGui::saveItem() noexcept {
|
ox::Error SceneEditorImGui::saveItem() noexcept {
|
||||||
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||||
oxReturnError(sctx->project->writeObj(m_itemPath, m_editor.scene()));
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ namespace nostalgia::scene {
|
|||||||
|
|
||||||
SceneEditor::SceneEditor(turbine::Context &ctx, ox::CRStringView path):
|
SceneEditor::SceneEditor(turbine::Context &ctx, ox::CRStringView path):
|
||||||
m_ctx(ctx),
|
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 {
|
const studio::Module *studioModule() noexcept {
|
||||||
return &mod;
|
return &mod;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
|
|||||||
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
||||||
oxRequireM(cctx, core::init(*tctx));
|
oxRequireM(cctx, core::init(*tctx));
|
||||||
constexpr ox::FileAddress SceneAddr = ox::StringLiteral("/Scenes/Chester.nscn");
|
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::setUpdateHandler(*tctx, updateHandler);
|
||||||
turbine::setKeyEventHandler(*tctx, keyEventHandler);
|
turbine::setKeyEventHandler(*tctx, keyEventHandler);
|
||||||
s_scene.emplace(*scn);
|
s_scene.emplace(*scn);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept {
|
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 {
|
void AboutPopup::open() noexcept {
|
||||||
|
@ -47,7 +47,7 @@ static ox::Error runApp(
|
|||||||
ox::CRStringView projectDataDir,
|
ox::CRStringView projectDataDir,
|
||||||
ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
||||||
oxRequireM(ctx, turbine::init(std::move(fs), appName));
|
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::setUpdateHandler(*ctx, updateHandler);
|
||||||
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
||||||
turbine::setConstantRefresh(*ctx, false);
|
turbine::setConstantRefresh(*ctx, false);
|
||||||
|
@ -26,7 +26,7 @@ class ProjectExplorer: public studio::Widget {
|
|||||||
ox::Error refreshProjectTreeModel(ox::CRStringView = {}) noexcept;
|
ox::Error refreshProjectTreeModel(ox::CRStringView = {}) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr ox::FileSystem *romFs() noexcept {
|
inline ox::FileSystem *romFs() noexcept {
|
||||||
return rom(*m_ctx);
|
return rom(*m_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ StudioUI::StudioUI(turbine::Context *ctx, ox::StringView projectDir) noexcept:
|
|||||||
ImGui::GetIO().IniFilename = nullptr;
|
ImGui::GetIO().IniFilename = nullptr;
|
||||||
loadModules();
|
loadModules();
|
||||||
// open project and files
|
// 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;
|
m_showProjectExplorer = config.showProjectExplorer;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
oxIgnoreError(openProject(config.projectPath));
|
oxIgnoreError(openProject(config.projectPath));
|
||||||
@ -223,7 +223,7 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
||||||
if (m_activeEditor != e.get()) {
|
if (m_activeEditor != e.get()) {
|
||||||
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();
|
config->activeTabItemName = m_activeEditor->itemName();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -276,7 +276,7 @@ void StudioUI::loadModules() noexcept {
|
|||||||
|
|
||||||
void StudioUI::toggleProjectExplorer() noexcept {
|
void StudioUI::toggleProjectExplorer() noexcept {
|
||||||
m_showProjectExplorer = !m_showProjectExplorer;
|
m_showProjectExplorer = !m_showProjectExplorer;
|
||||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||||
config->showProjectExplorer = m_showProjectExplorer;
|
config->showProjectExplorer = m_showProjectExplorer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -303,16 +303,16 @@ void StudioUI::save() noexcept {
|
|||||||
|
|
||||||
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
||||||
oxRequireM(fs, keel::loadRomFs(path));
|
oxRequireM(fs, keel::loadRomFs(path));
|
||||||
oxReturnError(keel::setRomFs(m_ctx->keelCtx, std::move(fs)));
|
oxReturnError(keel::setRomFs(keelCtx(*m_ctx), std::move(fs)));
|
||||||
turbine::setWindowTitle(*m_ctx, ox::sfmt("{} - {}", m_ctx->keelCtx.appName, path));
|
turbine::setWindowTitle(*m_ctx, ox::sfmt("{} - {}", keelCtx(*m_ctx).appName, path));
|
||||||
m_project = ox::make_unique<studio::Project>(m_ctx->keelCtx, ox::String(path), m_projectDir);
|
m_project = ox::make_unique<studio::Project>(keelCtx(*m_ctx), ox::String(path), m_projectDir);
|
||||||
auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||||
sctx->project = m_project.get();
|
sctx->project = m_project.get();
|
||||||
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||||
m_project->fileDeleted.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileDeleted.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||||
m_openFiles.clear();
|
m_openFiles.clear();
|
||||||
m_editors.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->projectPath = ox::String(path);
|
||||||
config->openFiles.clear();
|
config->openFiles.clear();
|
||||||
});
|
});
|
||||||
@ -362,7 +362,7 @@ ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab)
|
|||||||
m_activeEditorUpdatePending = editor;
|
m_activeEditorUpdatePending = editor;
|
||||||
}
|
}
|
||||||
// save to config
|
// save to config
|
||||||
studio::editConfig<StudioConfig>(&m_ctx->keelCtx, [&](StudioConfig *config) {
|
studio::editConfig<StudioConfig>(&keelCtx(*m_ctx), [&](StudioConfig *config) {
|
||||||
if (!config->openFiles.contains(path)) {
|
if (!config->openFiles.contains(path)) {
|
||||||
config->openFiles.emplace_back(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)));
|
oxIgnoreError(m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path)));
|
||||||
// save to config
|
// 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)));
|
oxIgnoreError(config->openFiles.erase(std::remove(config->openFiles.begin(), config->openFiles.end(), path)));
|
||||||
});
|
});
|
||||||
return OxError(0);
|
return OxError(0);
|
||||||
|
@ -33,7 +33,7 @@ class ItemMakerT: public ItemMaker {
|
|||||||
const T item;
|
const T item;
|
||||||
const ox::ClawFormat fmt;
|
const ox::ClawFormat fmt;
|
||||||
public:
|
public:
|
||||||
constexpr explicit ItemMakerT(
|
constexpr ItemMakerT(
|
||||||
ox::StringView pDisplayName,
|
ox::StringView pDisplayName,
|
||||||
ox::StringView pParentDir,
|
ox::StringView pParentDir,
|
||||||
ox::StringView fileExt,
|
ox::StringView fileExt,
|
||||||
@ -64,7 +64,7 @@ class ItemMakerT: public ItemMaker {
|
|||||||
ox::Error write(turbine::Context *ctx, ox::CRStringView pName) const noexcept override {
|
ox::Error write(turbine::Context *ctx, ox::CRStringView pName) const noexcept override {
|
||||||
const auto path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
const auto path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||||
auto sctx = turbine::applicationData<studio::StudioContext>(*ctx);
|
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);
|
return sctx->project->writeObj(path, item, fmt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,35 +1 @@
|
|||||||
add_library(
|
add_subdirectory(src)
|
||||||
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
|
|
||||||
)
|
|
@ -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::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;
|
class Context;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool buttonDown(const Context &ctx, Key) noexcept;
|
bool buttonDown(Context const&ctx, Key) noexcept;
|
||||||
|
|
||||||
using KeyEventHandler = void(*)(Context&, Key, bool);
|
using KeyEventHandler = void(*)(Context&, Key, bool);
|
||||||
|
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/memory.hpp>
|
||||||
#include <ox/fs/fs.hpp>
|
#include <ox/fs/fs.hpp>
|
||||||
|
|
||||||
#include "clipboard.hpp"
|
#include "clipboard.hpp"
|
||||||
@ -13,16 +14,14 @@
|
|||||||
|
|
||||||
namespace turbine {
|
namespace turbine {
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept;
|
ox::Result<ContextUPtr> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept;
|
||||||
|
|
||||||
void shutdown(Context &ctx) noexcept;
|
|
||||||
|
|
||||||
ox::Error run(Context &ctx) noexcept;
|
ox::Error run(Context &ctx) noexcept;
|
||||||
|
|
||||||
// Returns the number of milliseconds that have passed since the start of the
|
// Returns the number of milliseconds that have passed since the start of the
|
||||||
// program.
|
// program.
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
uint64_t ticksMs(const Context &ctx) noexcept;
|
uint64_t ticksMs(Context const&ctx) noexcept;
|
||||||
|
|
||||||
void requestShutdown(Context &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)
|
add_library(Turbine-GBA OBJECT)
|
||||||
target_sources(
|
target_sources(
|
||||||
Turbine-GBA PRIVATE
|
Turbine-GBA PRIVATE
|
||||||
|
context.cpp
|
||||||
clipboard.cpp
|
clipboard.cpp
|
||||||
|
event.cpp
|
||||||
gfx.cpp
|
gfx.cpp
|
||||||
irq.arm.cpp
|
irq.arm.cpp
|
||||||
turbine.arm.cpp
|
turbine.arm.cpp
|
||||||
turbine.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")
|
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||||
enable_language(ASM)
|
enable_language(ASM)
|
||||||
set_source_files_properties(turbine.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm)
|
set_source_files_properties(turbine.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||||
@ -23,6 +34,11 @@ else()
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
Turbine-GBA PUBLIC
|
||||||
|
../../include
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
Turbine-GBA PUBLIC
|
Turbine-GBA PUBLIC
|
||||||
TeaGBA
|
TeaGBA
|
@ -13,7 +13,6 @@ ox::String getClipboardText(Context&) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setClipboardText(Context&, ox::CRStringView) 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.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <turbine/event.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "event.hpp"
|
|
||||||
|
|
||||||
namespace turbine {
|
namespace turbine {
|
||||||
|
|
||||||
@ -11,4 +12,12 @@ void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept {
|
|||||||
ctx.updateHandler = h;
|
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};
|
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");
|
return OxError(1, "setWindowBounds not supported on GBA");
|
||||||
}
|
}
|
||||||
|
|
@ -16,8 +16,7 @@ static gba_timer_t g_wakeupTime{};
|
|||||||
|
|
||||||
ox::Error run(Context &ctx) noexcept {
|
ox::Error run(Context &ctx) noexcept {
|
||||||
g_wakeupTime = 0;
|
g_wakeupTime = 0;
|
||||||
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
|
while (ctx.running) {
|
||||||
while (gbaCtx->running) {
|
|
||||||
if (g_wakeupTime <= g_timerMs && ctx.updateHandler) {
|
if (g_wakeupTime <= g_timerMs && ctx.updateHandler) {
|
||||||
auto sleepTime = ctx.updateHandler(ctx);
|
auto sleepTime = ctx.updateHandler(ctx);
|
||||||
if (sleepTime >= 0) {
|
if (sleepTime >= 0) {
|
@ -6,6 +6,7 @@
|
|||||||
#include <teagba/irq.hpp>
|
#include <teagba/irq.hpp>
|
||||||
|
|
||||||
#include <keel/keel.hpp>
|
#include <keel/keel.hpp>
|
||||||
|
#include <turbine/context.hpp>
|
||||||
#include <turbine/gfx.hpp>
|
#include <turbine/gfx.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
@ -46,16 +47,17 @@ static ox::Result<std::size_t> findPreloadSection() noexcept {
|
|||||||
constexpr auto headerP2Len = ox_strlen(headerP1);
|
constexpr auto headerP2Len = ox_strlen(headerP1);
|
||||||
constexpr auto headerLen = headerP1Len + headerP2Len;
|
constexpr auto headerLen = headerP1Len + headerP2Len;
|
||||||
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
||||||
if (ox_memcmp(current, headerP1, headerP1Len) == 0 &&
|
if (memcmp(current, headerP1, headerP1Len) == 0 &&
|
||||||
ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
||||||
return reinterpret_cast<std::size_t>(current + headerLen);
|
return reinterpret_cast<std::size_t>(current + headerLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<turbine::Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
ox::Result<ContextUPtr> init(
|
||||||
auto ctx = ox::make_unique<gba::Context>();
|
ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
||||||
|
auto ctx = ox::make_unique<Context>();
|
||||||
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||||
#ifdef OX_BARE_METAL
|
#ifdef OX_BARE_METAL
|
||||||
oxReturnError(findPreloadSection().moveTo(&ctx->keelCtx.preloadSectionOffset));
|
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));
|
oxReturnError(initGfx(*ctx));
|
||||||
initTimer();
|
initTimer();
|
||||||
initIrq();
|
initIrq();
|
||||||
return ox::UPtr<turbine::Context>(std::move(ctx));
|
return ox::UPtr<turbine::Context, ContextDeleter>(std::move(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown(Context&) noexcept {
|
void shutdown(Context&) noexcept {
|
||||||
@ -73,13 +75,12 @@ uint64_t ticksMs(Context&) noexcept {
|
|||||||
return g_timerMs;
|
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)));
|
return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast<int>(k)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestShutdown(Context &ctx) noexcept {
|
void requestShutdown(Context &ctx) noexcept {
|
||||||
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
|
ctx.running = false;
|
||||||
gbaCtx->running = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <ox/std/types.hpp>
|
#include <ox/std/types.hpp>
|
||||||
|
|
||||||
#include "../config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
namespace turbine {
|
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 <glad/glad.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#if TURBINE_USE_IMGUI
|
||||||
#include <imgui_impl_glfw.h>
|
#include <imgui_impl_glfw.h>
|
||||||
#include <imgui_impl_opengl3.h>
|
#include <imgui_impl_opengl3.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <ox/std/defines.hpp>
|
#include <ox/std/defines.hpp>
|
||||||
|
|
||||||
#include <turbine/config.hpp>
|
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
namespace turbine {
|
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 {
|
namespace gl {
|
||||||
|
|
||||||
void addDrawer(Context &ctx, Drawer *cd) noexcept {
|
void addDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
ctx.drawers.emplace_back(cd);
|
||||||
gctx.drawers.emplace_back(cd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeDrawer(Context &ctx, Drawer *cd) noexcept {
|
void removeDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
for (auto i = 0u; i < ctx.drawers.size(); ++i) {
|
||||||
for (auto i = 0u; i < gctx.drawers.size(); ++i) {
|
if (ctx.drawers[i] == cd) {
|
||||||
if (gctx.drawers[i] == cd) {
|
oxIgnoreError(ctx.drawers.erase(i));
|
||||||
oxIgnoreError(gctx.drawers.erase(i));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,15 +36,12 @@ static void handleGlfwError(int err, const char *desc) noexcept {
|
|||||||
oxErrf("GLFW error ({}): {}\n", err, desc);
|
oxErrf("GLFW error ({}): {}\n", err, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto setKeyDownStatus(GlfwContext *gctx, Key key, bool down) noexcept {
|
static auto setKeyDownStatus(Context &ctx, Key key, bool down) noexcept {
|
||||||
if (down) {
|
ctx.keysDown &= ~(1llu << static_cast<int>(key));
|
||||||
gctx->keysDown |= 1llu << static_cast<int>(key);
|
ctx.keysDown |= static_cast<uint64_t>(down) << static_cast<int>(key);
|
||||||
} else {
|
|
||||||
gctx->keysDown &= ~(1llu << 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 = [] {
|
static constexpr auto keyMap = [] {
|
||||||
ox::Array<Key, GLFW_KEY_LAST> map = {};
|
ox::Array<Key, GLFW_KEY_LAST> map = {};
|
||||||
for (auto i = 0u; i < 26; ++i) {
|
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;
|
map[GLFW_KEY_ESCAPE] = Key::Escape;
|
||||||
return map;
|
return map;
|
||||||
}();
|
}();
|
||||||
const auto eventHandler = keyEventHandler(*ctx);
|
const auto eventHandler = keyEventHandler(ctx);
|
||||||
auto &gctx = glctx(*ctx);
|
|
||||||
const auto k = keyMap[static_cast<std::size_t>(key)];
|
const auto k = keyMap[static_cast<std::size_t>(key)];
|
||||||
setKeyDownStatus(&gctx, k, down);
|
setKeyDownStatus(ctx, k, down);
|
||||||
if (eventHandler) {
|
if (eventHandler) {
|
||||||
eventHandler(*ctx, k, down);
|
eventHandler(ctx, k, down);
|
||||||
}
|
}
|
||||||
//if constexpr(ox::defines::Debug) {
|
//if constexpr(ox::defines::Debug) {
|
||||||
// switch (key) {
|
// switch (key) {
|
||||||
@ -96,20 +81,21 @@ static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) 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;
|
ctx->uninterruptedRefreshes = 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept {
|
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;
|
ctx->uninterruptedRefreshes = 25;
|
||||||
if (action == GLFW_PRESS) {
|
if (action == GLFW_PRESS) {
|
||||||
handleKeyPress(ctx, key, true);
|
handleKeyPress(*ctx, key, true);
|
||||||
} else if (action == GLFW_RELEASE) {
|
} else if (action == GLFW_RELEASE) {
|
||||||
handleKeyPress(ctx, key, false);
|
handleKeyPress(*ctx, key, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TURBINE_USE_IMGUI
|
||||||
static void themeImgui() noexcept {
|
static void themeImgui() noexcept {
|
||||||
// Dark Ruda style by Raikiri from ImThemes
|
// Dark Ruda style by Raikiri from ImThemes
|
||||||
auto &style = ImGui::GetStyle();
|
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_NavWindowingDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.2000000029802322);
|
||||||
style.Colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355);
|
style.Colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ox::Error initGfx(Context &ctx) noexcept {
|
ox::Error initGfx(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
glfwSetErrorCallback(handleGlfwError);
|
glfwSetErrorCallback(handleGlfwError);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 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_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||||
}
|
}
|
||||||
glfwWindowHint(GLFW_RESIZABLE, 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;
|
constexpr auto Scale = 5;
|
||||||
gctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr.get(), nullptr, nullptr);
|
ctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||||
//gctx.window = glfwCreateWindow(868, 741, cstr.get(), nullptr, nullptr);
|
//ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||||
if (gctx.window == nullptr) {
|
if (ctx.window == nullptr) {
|
||||||
return OxError(1, "Could not open GLFW window");
|
return OxError(1, "Could not open GLFW window");
|
||||||
}
|
}
|
||||||
glfwSetCursorPosCallback(gctx.window, handleGlfwCursorPosEvent);
|
glfwSetCursorPosCallback(ctx.window, handleGlfwCursorPosEvent);
|
||||||
glfwSetMouseButtonCallback(gctx.window, handleGlfwMouseButtonEvent);
|
glfwSetMouseButtonCallback(ctx.window, handleGlfwMouseButtonEvent);
|
||||||
glfwSetKeyCallback(gctx.window, handleGlfwKeyEvent);
|
glfwSetKeyCallback(ctx.window, handleGlfwKeyEvent);
|
||||||
glfwSetWindowUserPointer(gctx.window, &ctx);
|
glfwSetWindowUserPointer(ctx.window, &ctx);
|
||||||
glfwMakeContextCurrent(gctx.window);
|
glfwMakeContextCurrent(ctx.window);
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
|
||||||
return OxError(2, "Could not init Glad");
|
return OxError(2, "Could not init Glad");
|
||||||
}
|
}
|
||||||
if constexpr(config::ImGuiEnabled) {
|
#if TURBINE_USE_IMGUI
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
auto &io = ImGui::GetIO();
|
auto &io = ImGui::GetIO();
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||||
//io.MouseDrawCursor = true;
|
//io.MouseDrawCursor = true;
|
||||||
ImGui_ImplGlfw_InitForOpenGL(gctx.window, true);
|
ImGui_ImplGlfw_InitForOpenGL(ctx.window, true);
|
||||||
ImGui_ImplOpenGL3_Init();
|
ImGui_ImplOpenGL3_Init();
|
||||||
themeImgui();
|
themeImgui();
|
||||||
}
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept {
|
void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
auto cstr = ox_malloca(title.bytes() + 1, char);
|
auto cstr = ox_malloca(title.bytes() + 1, char);
|
||||||
ox_strncpy(cstr.get(), title.data(), title.bytes());
|
ox_strncpy(cstr.get(), title.data(), title.bytes());
|
||||||
glfwSetWindowTitle(gctx.window, cstr.get());
|
glfwSetWindowTitle(ctx.window, cstr.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void focusWindow(Context &ctx) noexcept {
|
void focusWindow(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
glfwFocusWindow(ctx.window);
|
||||||
glfwFocusWindow(gctx.window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int getScreenWidth(Context &ctx) noexcept {
|
int getScreenWidth(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getScreenHeight(Context &ctx) noexcept {
|
int getScreenHeight(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Size getScreenSize(Context &ctx) noexcept {
|
ox::Size getScreenSize(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
glfwGetFramebufferSize(gctx.window, &w, &h);
|
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||||
return {w, h};
|
return {w, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Bounds getWindowBounds(Context &ctx) noexcept {
|
ox::Bounds getWindowBounds(Context &ctx) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
|
||||||
ox::Bounds bnds;
|
ox::Bounds bnds;
|
||||||
glfwGetWindowPos(gctx.window, &bnds.x, &bnds.y);
|
glfwGetWindowPos(ctx.window, &bnds.x, &bnds.y);
|
||||||
glfwGetWindowSize(gctx.window, &bnds.width, &bnds.height);
|
glfwGetWindowSize(ctx.window, &bnds.width, &bnds.height);
|
||||||
return bnds;
|
return bnds;
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error setWindowBounds(Context &ctx, const ox::Bounds &bnds) noexcept {
|
ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
glfwSetWindowPos(ctx.window, bnds.x, bnds.y);
|
||||||
glfwSetWindowPos(gctx.window, bnds.x, bnds.y);
|
glfwSetWindowSize(ctx.window, bnds.width, bnds.height);
|
||||||
glfwSetWindowSize(gctx.window, bnds.width, bnds.height);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConstantRefresh(Context &ctx, bool r) noexcept {
|
void setConstantRefresh(Context &ctx, bool r) noexcept {
|
||||||
auto &gctx = glctx(ctx);
|
ctx.constantRefresh = r;
|
||||||
gctx.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…
x
Reference in New Issue
Block a user