[olympic] Move keel, turbine, and studio to olympic
This commit is contained in:
1
src/olympic/turbine/CMakeLists.txt
Normal file
1
src/olympic/turbine/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(src)
|
50
src/olympic/turbine/include/turbine/clipboard.hpp
Normal file
50
src/olympic/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(ox::StringView name, int 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/olympic/turbine/include/turbine/context.hpp
Normal file
56
src/olympic/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;
|
||||
|
||||
}
|
||||
|
17
src/olympic/turbine/include/turbine/event.hpp
Normal file
17
src/olympic/turbine/include/turbine/event.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class Context;
|
||||
|
||||
using UpdateHandler = int(*)(Context&);
|
||||
|
||||
// Sets event handler that sleeps for the time given in the return value. The
|
||||
// sleep time is a minimum of ~16 milliseconds.
|
||||
void setUpdateHandler(Context &ctx, UpdateHandler) noexcept;
|
||||
|
||||
}
|
48
src/olympic/turbine/include/turbine/gfx.hpp
Normal file
48
src/olympic/turbine/include/turbine/gfx.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/point.hpp>
|
||||
#include <ox/std/size.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/model/def.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
namespace gl {
|
||||
class Drawer {
|
||||
public:
|
||||
virtual ~Drawer() = default;
|
||||
virtual void draw(Context&) noexcept = 0;
|
||||
};
|
||||
void addDrawer(Context &ctx, Drawer *cd) noexcept;
|
||||
void removeDrawer(Context &ctx, Drawer *cd) noexcept;
|
||||
}
|
||||
|
||||
ox::Error initGfx(Context &ctx) noexcept;
|
||||
|
||||
void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept;
|
||||
|
||||
void focusWindow(Context &ctx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int getScreenWidth(Context &ctx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int getScreenHeight(Context &ctx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Size getScreenSize(Context &ctx) noexcept;
|
||||
|
||||
ox::Bounds getWindowBounds(Context &ctx) noexcept;
|
||||
|
||||
ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept;
|
||||
|
||||
void setConstantRefresh(Context &ctx, bool r) noexcept;
|
||||
|
||||
}
|
79
src/olympic/turbine/include/turbine/input.hpp
Normal file
79
src/olympic/turbine/include/turbine/input.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/defines.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
enum Key {
|
||||
// GBA implementation currently relies on GamePad entry order
|
||||
GamePad_A = 0,
|
||||
GamePad_B,
|
||||
GamePad_Select,
|
||||
GamePad_Start,
|
||||
GamePad_Right,
|
||||
GamePad_Left,
|
||||
GamePad_Up,
|
||||
GamePad_Down,
|
||||
GamePad_R,
|
||||
GamePad_L,
|
||||
|
||||
Num_0,
|
||||
Num_1,
|
||||
Num_2,
|
||||
Num_3,
|
||||
Num_4,
|
||||
Num_5,
|
||||
Num_6,
|
||||
Num_7,
|
||||
Num_8,
|
||||
Num_9,
|
||||
|
||||
Alpha_A,
|
||||
Alpha_B,
|
||||
Alpha_C,
|
||||
Alpha_D,
|
||||
Alpha_E,
|
||||
Alpha_F,
|
||||
Alpha_G,
|
||||
Alpha_H,
|
||||
Alpha_I,
|
||||
Alpha_J,
|
||||
Alpha_K,
|
||||
Alpha_L,
|
||||
Alpha_M,
|
||||
Alpha_N,
|
||||
Alpha_O,
|
||||
Alpha_P,
|
||||
Alpha_Q,
|
||||
Alpha_R,
|
||||
Alpha_S,
|
||||
Alpha_T,
|
||||
Alpha_U,
|
||||
Alpha_V,
|
||||
Alpha_W,
|
||||
Alpha_X,
|
||||
Alpha_Y,
|
||||
Alpha_Z,
|
||||
|
||||
Mod_Alt,
|
||||
Mod_Ctrl,
|
||||
Mod_Super,
|
||||
Mod_Shift,
|
||||
|
||||
Escape,
|
||||
|
||||
End
|
||||
};
|
||||
|
||||
class Context;
|
||||
|
||||
[[nodiscard]]
|
||||
bool buttonDown(Context const&ctx, Key) noexcept;
|
||||
|
||||
using KeyEventHandler = void(*)(Context&, Key, bool);
|
||||
|
||||
}
|
28
src/olympic/turbine/include/turbine/turbine.hpp
Normal file
28
src/olympic/turbine/include/turbine/turbine.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include "clipboard.hpp"
|
||||
#include "event.hpp"
|
||||
#include "gfx.hpp"
|
||||
#include "input.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
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(Context const&ctx) noexcept;
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept;
|
||||
|
||||
}
|
40
src/olympic/turbine/src/CMakeLists.txt
Normal file
40
src/olympic/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
|
||||
)
|
46
src/olympic/turbine/src/gba/CMakeLists.txt
Normal file
46
src/olympic/turbine/src/gba/CMakeLists.txt
Normal file
@ -0,0 +1,46 @@
|
||||
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)
|
||||
target_sources(
|
||||
Turbine-GBA PRIVATE
|
||||
irq.s
|
||||
)
|
||||
target_link_libraries(Turbine PUBLIC Turbine-GBA)
|
||||
else()
|
||||
target_sources(
|
||||
Turbine-GBA PRIVATE
|
||||
irqstub.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
Turbine-GBA PUBLIC
|
||||
../../include
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Turbine-GBA PUBLIC
|
||||
TeaGBA
|
||||
Keel
|
||||
)
|
18
src/olympic/turbine/src/gba/clipboard.cpp
Normal file
18
src/olympic/turbine/src/gba/clipboard.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::String getClipboardText(Context&) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void setClipboardText(Context&, ox::CRStringView) noexcept {
|
||||
}
|
||||
|
||||
}
|
12
src/olympic/turbine/src/gba/config.hpp
Normal file
12
src/olympic/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/olympic/turbine/src/gba/context.cpp
Normal file
29
src/olympic/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/olympic/turbine/src/gba/context.hpp
Normal file
36
src/olympic/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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
23
src/olympic/turbine/src/gba/event.cpp
Normal file
23
src/olympic/turbine/src/gba/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;
|
||||
}
|
||||
|
||||
}
|
50
src/olympic/turbine/src/gba/gfx.cpp
Normal file
50
src/olympic/turbine/src/gba/gfx.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ox/std/size.hpp>
|
||||
#include <ox/std/stringview.hpp>
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/gfx.hpp>
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::Error initGfx(Context&) noexcept {
|
||||
REG_DISPCTL = teagba::DispCtl_Mode0
|
||||
| teagba::DispCtl_SpriteMap1D
|
||||
| teagba::DispCtl_Obj;
|
||||
// tell display to trigger vblank interrupts
|
||||
REG_DISPSTAT = REG_DISPSTAT | teagba::DispStat_irq_vblank;
|
||||
// enable vblank interrupt
|
||||
REG_IE = REG_IE | teagba::Int_vblank;
|
||||
return {};
|
||||
}
|
||||
|
||||
void setWindowTitle(Context&, ox::CRStringView) noexcept {
|
||||
}
|
||||
|
||||
int getScreenWidth(Context&) noexcept {
|
||||
return 240;
|
||||
}
|
||||
|
||||
int getScreenHeight(Context&) noexcept {
|
||||
return 160;
|
||||
}
|
||||
|
||||
ox::Size getScreenSize(Context&) noexcept {
|
||||
return {240, 160};
|
||||
}
|
||||
|
||||
ox::Bounds getWindowBounds(Context&) noexcept {
|
||||
return {0, 0, 240, 160};
|
||||
}
|
||||
|
||||
ox::Error setWindowBounds(Context&, ox::Bounds const&) noexcept {
|
||||
return OxError(1, "setWindowBounds not supported on GBA");
|
||||
}
|
||||
|
||||
}
|
36
src/olympic/turbine/src/gba/irq.arm.cpp
Normal file
36
src/olympic/turbine/src/gba/irq.arm.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in
|
||||
// here
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/gfx.hpp>
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include "turbine.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
volatile gba_timer_t g_timerMs = 0;
|
||||
|
||||
}
|
||||
|
||||
using namespace turbine;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void turbine_isr_vblank() noexcept {
|
||||
teagba::applySpriteUpdates();
|
||||
if constexpr(config::GbaEventLoopTimerBased) {
|
||||
// disable vblank interrupt until it is needed again
|
||||
REG_IE = REG_IE & ~teagba::Int_vblank;
|
||||
}
|
||||
}
|
||||
|
||||
void turbine_isr_timer0() noexcept {
|
||||
g_timerMs = g_timerMs + 1;
|
||||
}
|
||||
|
||||
}
|
110
src/olympic/turbine/src/gba/irq.s
Normal file
110
src/olympic/turbine/src/gba/irq.s
Normal file
@ -0,0 +1,110 @@
|
||||
//
|
||||
// Copyright 2016 - 2023 gary@drinkingtea.net
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
|
||||
.section .iwram, "ax", %progbits
|
||||
.arm
|
||||
.align
|
||||
|
||||
.extern turbine_isr_vblank
|
||||
.extern turbine_isr_timer0
|
||||
|
||||
.equ REG_IFBIOS, 0x03007ff8
|
||||
|
||||
.equ REG_IE, 0x04000200
|
||||
.equ REG_IF, 0x04000202
|
||||
.equ REG_IME, 0x04000208
|
||||
|
||||
.equ Int_vblank, 1
|
||||
.equ Int_hblank, 2
|
||||
.equ Int_vcount, 4
|
||||
.equ Int_timer0, 8
|
||||
.equ Int_timer1, 16
|
||||
.equ Int_timer2, 32
|
||||
.equ Int_timer3, 64
|
||||
.equ Int_serial, 128 // link cable
|
||||
.equ Int_dma0, 256
|
||||
.equ Int_dma1, 512
|
||||
.equ Int_dma2, 1024
|
||||
.equ Int_dma3, 2048
|
||||
.equ Int_dma4, 4096
|
||||
.equ Int_dma5, 8192
|
||||
.equ Int_input, 16384 // gamepad
|
||||
.equ Int_cart, 32768 // cartridge removed
|
||||
|
||||
|
||||
.global turbine_isr
|
||||
.type turbine_isr, %function
|
||||
turbine_isr:
|
||||
// read IE
|
||||
ldr r0, =#REG_IE
|
||||
ldrh r1, [r0] // r1 becomes IE value
|
||||
ldrh r2, [r0, #2] // r2 becomes IF value
|
||||
and r1, r1, r2 // r1 becomes IE & IF
|
||||
// done with r2 as IF value
|
||||
|
||||
// Acknowledge IRQ in REG_IF
|
||||
strh r1, [r0, #2]
|
||||
ldr r0, =#REG_IFBIOS
|
||||
// Acknowledge IRQ in REG_IFBIOS
|
||||
ldr r2, [r0] // r2 becomes REG_IFBIOS value
|
||||
orr r2, r2, r1
|
||||
str r2, [r0]
|
||||
// done with r2 as IFBIOS value
|
||||
// done with r0 as REG_IFBIOS
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Interrupt Table Begin //
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
cmp r1, #Int_timer0
|
||||
ldreq r0, =turbine_isr_timer0
|
||||
beq isr_call_handler
|
||||
|
||||
cmp r1, #Int_vblank
|
||||
ldreq r0, =turbine_isr_vblank
|
||||
beq isr_call_handler
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Interrupt Table End //
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
bx lr
|
||||
|
||||
isr_call_handler:
|
||||
// clear IME to disable interrupts
|
||||
ldr r2, =#REG_IME
|
||||
mov r1, #0
|
||||
str r1, [r2]
|
||||
|
||||
// enter system mode
|
||||
mrs r1, cpsr
|
||||
bic r1, r1, #0xDF
|
||||
orr r1, r1, #0x1F
|
||||
msr cpsr, r1
|
||||
|
||||
push {lr}
|
||||
ldr lr, =isr_restore
|
||||
bx r0
|
||||
|
||||
isr_restore:
|
||||
pop {lr}
|
||||
|
||||
// re-enter irq mode
|
||||
mrs r0, cpsr
|
||||
bic r0, r0, #0xDF
|
||||
orr r0, r0, #0x92
|
||||
msr cpsr, r0
|
||||
|
||||
// set IME to re-enable interrupts
|
||||
ldr r2, =#REG_IME
|
||||
mov r0, #1
|
||||
str r0, [r2]
|
||||
|
||||
bx lr
|
||||
|
||||
// vim: ft=armv4
|
8
src/olympic/turbine/src/gba/irqstub.cpp
Normal file
8
src/olympic/turbine/src/gba/irqstub.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
// stub for building TeaGBA for PC targets, for purposes of not having to
|
||||
// switch back and forth between builds when editing GBA files
|
||||
|
||||
extern "C" void turbine_isr() {}
|
40
src/olympic/turbine/src/gba/turbine.arm.cpp
Normal file
40
src/olympic/turbine/src/gba/turbine.arm.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/bios.hpp>
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "turbine.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
extern volatile gba_timer_t g_timerMs;
|
||||
static gba_timer_t g_wakeupTime{};
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
g_wakeupTime = 0;
|
||||
while (ctx.running) {
|
||||
if (g_wakeupTime <= g_timerMs && ctx.updateHandler) {
|
||||
auto sleepTime = ctx.updateHandler(ctx);
|
||||
if (sleepTime >= 0) {
|
||||
g_wakeupTime = g_timerMs + static_cast<unsigned>(sleepTime);
|
||||
} else {
|
||||
g_wakeupTime = ~gba_timer_t(0);
|
||||
}
|
||||
}
|
||||
if constexpr(config::GbaEventLoopTimerBased) {
|
||||
// wait for timer interrupt
|
||||
teagba_intrwait(
|
||||
0,
|
||||
teagba::Int_timer0 | teagba::Int_timer1 | teagba::Int_timer2 | teagba::Int_timer3);
|
||||
} else {
|
||||
teagba_vblankintrwait();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
86
src/olympic/turbine/src/gba/turbine.cpp
Normal file
86
src/olympic/turbine/src/gba/turbine.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "turbine.hpp"
|
||||
|
||||
extern "C" void turbine_isr();
|
||||
|
||||
namespace turbine {
|
||||
|
||||
// Timer Consts
|
||||
constexpr int NanoSecond = 1'000'000'000;
|
||||
constexpr int MilliSecond = 1000;
|
||||
constexpr int TicksMs59ns =
|
||||
65535 - static_cast<uint16_t>(static_cast<double>(NanoSecond / MilliSecond) / 59.59);
|
||||
|
||||
extern volatile gba_timer_t g_timerMs;
|
||||
|
||||
static void initIrq() noexcept {
|
||||
REG_ISR = turbine_isr;
|
||||
REG_IME = 1; // enable interrupts
|
||||
}
|
||||
|
||||
static void initTimer() noexcept {
|
||||
// make timer0 a ~1 millisecond timer
|
||||
REG_TIMER0 = TicksMs59ns;
|
||||
REG_TIMER0CTL = 0b11000000;
|
||||
// enable interrupt for timer0
|
||||
REG_IE = REG_IE | teagba::Int_timer0;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static ox::Result<std::size_t> findPreloadSection() noexcept {
|
||||
// put the header in the wrong order to prevent mistaking this code for the
|
||||
// media section
|
||||
constexpr auto headerP2 = "DER_____________";
|
||||
constexpr auto headerP1 = "KEEL_PRELOAD_HEA";
|
||||
constexpr auto headerP1Len = ox_strlen(headerP2);
|
||||
constexpr auto headerP2Len = ox_strlen(headerP1);
|
||||
constexpr auto headerLen = headerP1Len + headerP2Len;
|
||||
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
||||
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<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));
|
||||
#endif
|
||||
oxReturnError(initGfx(*ctx));
|
||||
initTimer();
|
||||
initIrq();
|
||||
return ox::UPtr<turbine::Context, ContextDeleter>(std::move(ctx));
|
||||
}
|
||||
|
||||
void shutdown(Context&) noexcept {
|
||||
}
|
||||
|
||||
uint64_t ticksMs(Context&) noexcept {
|
||||
return g_timerMs;
|
||||
}
|
||||
|
||||
bool buttonDown(Context const&, Key k) noexcept {
|
||||
return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast<int>(k)));
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
ctx.running = false;
|
||||
}
|
||||
|
||||
}
|
15
src/olympic/turbine/src/gba/turbine.hpp
Normal file
15
src/olympic/turbine/src/gba/turbine.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
using gba_timer_t = ox::Uint<config::GbaTimerBits>;
|
||||
|
||||
}
|
24
src/olympic/turbine/src/glfw/CMakeLists.txt
Normal file
24
src/olympic/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/olympic/turbine/src/glfw/clipboard.cpp
Normal file
36
src/olympic/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/olympic/turbine/src/glfw/config.hpp
Normal file
11
src/olympic/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/olympic/turbine/src/glfw/context.cpp
Normal file
31
src/olympic/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/olympic/turbine/src/glfw/context.hpp
Normal file
41
src/olympic/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/olympic/turbine/src/glfw/event.cpp
Normal file
23
src/olympic/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;
|
||||
}
|
||||
|
||||
}
|
265
src/olympic/turbine/src/glfw/gfx.cpp
Normal file
265
src/olympic/turbine/src/glfw/gfx.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#if TURBINE_USE_IMGUI
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#endif
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
namespace gl {
|
||||
|
||||
void addDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||
ctx.drawers.emplace_back(cd);
|
||||
}
|
||||
|
||||
void removeDrawer(Context &ctx, Drawer *cd) noexcept {
|
||||
for (auto i = 0u; i < ctx.drawers.size(); ++i) {
|
||||
if (ctx.drawers[i] == cd) {
|
||||
oxIgnoreError(ctx.drawers.erase(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void handleGlfwError(int err, const char *desc) noexcept {
|
||||
oxErrf("GLFW error ({}): {}\n", err, desc);
|
||||
}
|
||||
|
||||
static auto setKeyDownStatus(Context &ctx, Key key, bool down) noexcept {
|
||||
ctx.keysDown &= ~(1llu << static_cast<int>(key));
|
||||
ctx.keysDown |= static_cast<uint64_t>(down) << static_cast<int>(key);
|
||||
}
|
||||
|
||||
static void handleKeyPress(Context &ctx, int key, bool down) noexcept {
|
||||
static constexpr auto keyMap = [] {
|
||||
ox::Array<Key, GLFW_KEY_LAST> map = {};
|
||||
for (auto i = 0u; i < 26; ++i) {
|
||||
map[GLFW_KEY_A + i] = static_cast<Key>(static_cast<unsigned>(Key::Alpha_A) + i);
|
||||
}
|
||||
for (auto i = 0u; i < 10; ++i) {
|
||||
map[GLFW_KEY_0 + i] = static_cast<Key>(static_cast<unsigned>(Key::Num_0) + i);
|
||||
}
|
||||
map[GLFW_KEY_LEFT_ALT] = Key::Mod_Alt;
|
||||
map[GLFW_KEY_RIGHT_ALT] = Key::Mod_Alt;
|
||||
map[GLFW_KEY_LEFT_CONTROL] = Key::Mod_Ctrl;
|
||||
map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl;
|
||||
map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super;
|
||||
map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super;
|
||||
map[GLFW_KEY_ESCAPE] = Key::Escape;
|
||||
return map;
|
||||
}();
|
||||
const auto eventHandler = keyEventHandler(ctx);
|
||||
const auto k = keyMap[static_cast<std::size_t>(key)];
|
||||
setKeyDownStatus(ctx, k, down);
|
||||
if (eventHandler) {
|
||||
eventHandler(ctx, k, down);
|
||||
}
|
||||
}
|
||||
|
||||
static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
||||
}
|
||||
|
||||
static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept {
|
||||
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<Context*>(glfwGetWindowUserPointer(window));
|
||||
ctx->uninterruptedRefreshes = 25;
|
||||
if (action == GLFW_PRESS) {
|
||||
handleKeyPress(*ctx, key, true);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
handleKeyPress(*ctx, key, false);
|
||||
}
|
||||
}
|
||||
|
||||
#if TURBINE_USE_IMGUI
|
||||
static void themeImgui() noexcept {
|
||||
// Dark Ruda style by Raikiri from ImThemes
|
||||
auto &style = ImGui::GetStyle();
|
||||
style.Alpha = 1.0;
|
||||
style.DisabledAlpha = 0.6000000238418579;
|
||||
style.WindowPadding = ImVec2(8.0, 8.0);
|
||||
style.WindowRounding = 0.0;
|
||||
style.WindowBorderSize = 1.0;
|
||||
style.WindowMinSize = ImVec2(32.0, 32.0);
|
||||
style.WindowTitleAlign = ImVec2(0.0, 0.5);
|
||||
style.WindowMenuButtonPosition = ImGuiDir_Left;
|
||||
style.ChildRounding = 0.0;
|
||||
style.ChildBorderSize = 1.0;
|
||||
style.PopupRounding = 0.0;
|
||||
style.PopupBorderSize = 1.0;
|
||||
style.FramePadding = ImVec2(4.0, 3.0);
|
||||
// custom value
|
||||
style.FrameRounding = 3.0;
|
||||
style.FrameBorderSize = 0.0;
|
||||
style.ItemSpacing = ImVec2(8.0, 4.0);
|
||||
style.ItemInnerSpacing = ImVec2(4.0, 4.0);
|
||||
style.CellPadding = ImVec2(4.0, 2.0);
|
||||
style.IndentSpacing = 21.0;
|
||||
style.ColumnsMinSpacing = 6.0;
|
||||
style.ScrollbarSize = 14.0;
|
||||
style.ScrollbarRounding = 9.0;
|
||||
style.GrabMinSize = 10.0;
|
||||
style.GrabRounding = 4.0;
|
||||
style.TabRounding = 4.0;
|
||||
style.TabBorderSize = 0.0;
|
||||
style.TabMinWidthForCloseButton = 0.0;
|
||||
style.ColorButtonPosition = ImGuiDir_Right;
|
||||
style.ButtonTextAlign = ImVec2(0.5, 0.5);
|
||||
style.SelectableTextAlign = ImVec2(0.0, 0.0);
|
||||
// colors
|
||||
constexpr auto imVec4 = [](double r, double g, double b, double a) {
|
||||
return ImVec4(
|
||||
static_cast<float>(r),
|
||||
static_cast<float>(g),
|
||||
static_cast<float>(b),
|
||||
static_cast<float>(a));
|
||||
};
|
||||
style.Colors[ImGuiCol_Text] = imVec4(0.9490196108818054, 0.95686274766922, 0.9764705896377563, 1.0);
|
||||
style.Colors[ImGuiCol_TextDisabled] = imVec4(0.3568627536296844, 0.4196078479290009, 0.4666666686534882, 1.0);
|
||||
style.Colors[ImGuiCol_WindowBg] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
|
||||
style.Colors[ImGuiCol_ChildBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0);
|
||||
style.Colors[ImGuiCol_PopupBg] = imVec4(0.0784313753247261, 0.0784313753247261, 0.0784313753247261, 0.9399999976158142);
|
||||
style.Colors[ImGuiCol_Border] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0);
|
||||
style.Colors[ImGuiCol_BorderShadow] = imVec4(0.0, 0.0, 0.0, 0.0);
|
||||
style.Colors[ImGuiCol_FrameBg] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = imVec4(0.1176470592617989, 0.2000000029802322, 0.2784313857555389, 1.0);
|
||||
style.Colors[ImGuiCol_FrameBgActive] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 1.0);
|
||||
style.Colors[ImGuiCol_TitleBg] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 0.6499999761581421);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = imVec4(0.0, 0.0, 0.0, 0.5099999904632568);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0);
|
||||
style.Colors[ImGuiCol_ScrollbarBg] = imVec4(0.01960784383118153, 0.01960784383118153, 0.01960784383118153, 0.3899999856948853);
|
||||
style.Colors[ImGuiCol_ScrollbarGrab] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabHovered] = imVec4(0.1764705926179886, 0.2196078449487686, 0.2470588237047195, 1.0);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabActive] = imVec4(0.08627451211214066, 0.2078431397676468, 0.3098039329051971, 1.0);
|
||||
style.Colors[ImGuiCol_CheckMark] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
|
||||
style.Colors[ImGuiCol_SliderGrab] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = imVec4(0.3686274588108063, 0.6078431606292725, 1.0, 1.0);
|
||||
style.Colors[ImGuiCol_Button] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
|
||||
style.Colors[ImGuiCol_ButtonHovered] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
|
||||
style.Colors[ImGuiCol_ButtonActive] = imVec4(0.05882352963089943, 0.529411792755127, 0.9764705896377563, 1.0);
|
||||
// custom value
|
||||
style.Colors[ImGuiCol_Header] = imVec4(0.4000000029802322, 0.4470588237047195, 0.4862745225429535, 0.550000011920929);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929);
|
||||
style.Colors[ImGuiCol_HeaderActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0);
|
||||
style.Colors[ImGuiCol_Separator] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
|
||||
style.Colors[ImGuiCol_SeparatorHovered] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 0.7799999713897705);
|
||||
style.Colors[ImGuiCol_SeparatorActive] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 1.0);
|
||||
style.Colors[ImGuiCol_ResizeGrip] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.25);
|
||||
style.Colors[ImGuiCol_ResizeGripHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.6700000166893005);
|
||||
style.Colors[ImGuiCol_ResizeGripActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.949999988079071);
|
||||
style.Colors[ImGuiCol_Tab] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
|
||||
style.Colors[ImGuiCol_TabHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929);
|
||||
style.Colors[ImGuiCol_TabActive] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
|
||||
style.Colors[ImGuiCol_TabUnfocused] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
|
||||
style.Colors[ImGuiCol_TabUnfocusedActive] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
|
||||
style.Colors[ImGuiCol_PlotLines] = imVec4(0.6078431606292725, 0.6078431606292725, 0.6078431606292725, 1.0);
|
||||
style.Colors[ImGuiCol_PlotLinesHovered] = imVec4(1.0, 0.4274509847164154, 0.3490196168422699, 1.0);
|
||||
style.Colors[ImGuiCol_PlotHistogram] = imVec4(0.8980392217636108, 0.6980392336845398, 0.0, 1.0);
|
||||
style.Colors[ImGuiCol_PlotHistogramHovered] = imVec4(1.0, 0.6000000238418579, 0.0, 1.0);
|
||||
style.Colors[ImGuiCol_TableHeaderBg] = imVec4(0.1882352977991104, 0.1882352977991104, 0.2000000029802322, 1.0);
|
||||
style.Colors[ImGuiCol_TableBorderStrong] = imVec4(0.3098039329051971, 0.3098039329051971, 0.3490196168422699, 1.0);
|
||||
style.Colors[ImGuiCol_TableBorderLight] = imVec4(0.2274509817361832, 0.2274509817361832, 0.2470588237047195, 1.0);
|
||||
style.Colors[ImGuiCol_TableRowBg] = imVec4(0.0, 0.0, 0.0, 0.0);
|
||||
style.Colors[ImGuiCol_TableRowBgAlt] = imVec4(1.0, 1.0, 1.0, 0.05999999865889549);
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.3499999940395355);
|
||||
style.Colors[ImGuiCol_DragDropTarget] = imVec4(1.0, 1.0, 0.0, 0.8999999761581421);
|
||||
style.Colors[ImGuiCol_NavHighlight] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0);
|
||||
style.Colors[ImGuiCol_NavWindowingHighlight] = imVec4(1.0, 1.0, 1.0, 0.699999988079071);
|
||||
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 {
|
||||
glfwSetErrorCallback(handleGlfwError);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
constexpr auto Scale = 5;
|
||||
ctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||
//ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||
if (ctx.window == nullptr) {
|
||||
return OxError(1, "Could not open GLFW 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 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 cstr = ox_malloca(title.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), title.data(), title.bytes());
|
||||
glfwSetWindowTitle(ctx.window, cstr.get());
|
||||
}
|
||||
|
||||
void focusWindow(Context &ctx) noexcept {
|
||||
glfwFocusWindow(ctx.window);
|
||||
}
|
||||
|
||||
int getScreenWidth(Context &ctx) noexcept {
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
int getScreenHeight(Context &ctx) noexcept {
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
ox::Size getScreenSize(Context &ctx) noexcept {
|
||||
int w = 0, h = 0;
|
||||
glfwGetFramebufferSize(ctx.window, &w, &h);
|
||||
return {w, h};
|
||||
}
|
||||
|
||||
ox::Bounds getWindowBounds(Context &ctx) noexcept {
|
||||
ox::Bounds bnds;
|
||||
glfwGetWindowPos(ctx.window, &bnds.x, &bnds.y);
|
||||
glfwGetWindowSize(ctx.window, &bnds.width, &bnds.height);
|
||||
return bnds;
|
||||
}
|
||||
|
||||
ox::Error setWindowBounds(Context &ctx, ox::Bounds const&bnds) noexcept {
|
||||
glfwSetWindowPos(ctx.window, bnds.x, bnds.y);
|
||||
glfwSetWindowSize(ctx.window, bnds.width, bnds.height);
|
||||
return {};
|
||||
}
|
||||
|
||||
void setConstantRefresh(Context &ctx, bool r) noexcept {
|
||||
ctx.constantRefresh = r;
|
||||
}
|
||||
|
||||
}
|
123
src/olympic/turbine/src/glfw/turbine.cpp
Normal file
123
src/olympic/turbine/src/glfw/turbine.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#if TURBINE_USE_IMGUI
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#endif
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
static void draw(Context &ctx) noexcept {
|
||||
// draw start
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
#endif
|
||||
for (auto d : ctx.drawers) {
|
||||
d->draw(ctx);
|
||||
}
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
#endif
|
||||
// draw end
|
||||
glfwSwapBuffers(ctx.window);
|
||||
}
|
||||
|
||||
static void draw(GLFWwindow *window, int, int) noexcept {
|
||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||
draw(ctx);
|
||||
}
|
||||
|
||||
ox::Result<ContextUPtr> init(
|
||||
ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
||||
auto ctx = ox::make_unique<Context>();
|
||||
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
|
||||
using namespace std::chrono;
|
||||
ctx->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
glfwInit();
|
||||
oxReturnError(initGfx(*ctx));
|
||||
glfwSetWindowSizeCallback(ctx->window, draw);
|
||||
return ox::UPtr<Context, ContextDeleter>(ctx.release());
|
||||
}
|
||||
|
||||
static void tickFps(Context &ctx, uint64_t nowMs) noexcept {
|
||||
++ctx.draws;
|
||||
if (ctx.draws >= 500) {
|
||||
const auto duration = static_cast<double>(nowMs - ctx.prevFpsCheckTime) / 1000.0;
|
||||
const auto fps = static_cast<int>(static_cast<double>(ctx.draws) / duration);
|
||||
if constexpr(config::GlFpsPrint) {
|
||||
oxOutf("FPS: {}\n", fps);
|
||||
}
|
||||
oxTracef("turbine.fps", "FPS: {}", fps);
|
||||
ctx.prevFpsCheckTime = nowMs;
|
||||
ctx.draws = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
int sleepTime = 0;
|
||||
while (!glfwWindowShouldClose(ctx.window)) {
|
||||
glfwPollEvents();
|
||||
const auto ticks = ticksMs(ctx);
|
||||
if (ctx.wakeupTime <= ticks) {
|
||||
sleepTime = ctx.updateHandler(ctx);
|
||||
if (sleepTime >= 0) {
|
||||
ctx.wakeupTime = ticks + static_cast<unsigned>(sleepTime);
|
||||
} else {
|
||||
ctx.wakeupTime = ~uint64_t(0);
|
||||
}
|
||||
} else {
|
||||
sleepTime = 10;
|
||||
}
|
||||
tickFps(ctx, ticks);
|
||||
draw(ctx);
|
||||
if (!ctx.constantRefresh) {
|
||||
if (ctx.uninterruptedRefreshes) {
|
||||
--ctx.uninterruptedRefreshes;
|
||||
} else {
|
||||
glfwWaitEventsTimeout(sleepTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
shutdown(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
void shutdown(Context &ctx) noexcept {
|
||||
if (ctx.window) {
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
#endif
|
||||
glfwDestroyWindow(ctx.window);
|
||||
ctx.window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ticksMs(Context const&ctx) noexcept {
|
||||
using namespace std::chrono;
|
||||
const auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
return static_cast<uint64_t>(now - ctx.startTime);
|
||||
}
|
||||
|
||||
bool buttonDown(Context const&ctx, Key key) noexcept {
|
||||
return (ctx.keysDown >> static_cast<int>(key)) & 1;
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
glfwSetWindowShouldClose(ctx.window, true);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user