2026-02-25 06:23:53 +08:00
|
|
|
|
#include <app/application.h>
|
2026-02-28 20:56:11 +08:00
|
|
|
|
#include <context/context.h>
|
2026-02-28 04:44:56 +08:00
|
|
|
|
#include <event/events.h>
|
2026-02-27 22:59:17 +08:00
|
|
|
|
#include <platform/sdl2.h>
|
2026-02-28 22:30:48 +08:00
|
|
|
|
#include <platform/window.h>
|
|
|
|
|
|
#include <platform/input.h>
|
2026-02-25 06:23:53 +08:00
|
|
|
|
#include <utils/logger.h>
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __SWITCH__
|
|
|
|
|
|
#include <switch.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
namespace extra2d {
|
|
|
|
|
|
|
2026-02-27 22:59:17 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取当前时间(秒)
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
static double getTimeSeconds() {
|
|
|
|
|
|
#ifdef __SWITCH__
|
2026-02-27 23:08:49 +08:00
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
|
|
return static_cast<double>(ts.tv_sec) +
|
|
|
|
|
|
static_cast<double>(ts.tv_nsec) / 1000000000.0;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
#else
|
2026-02-27 23:08:49 +08:00
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
|
auto now = steady_clock::now();
|
|
|
|
|
|
auto duration = now.time_since_epoch();
|
|
|
|
|
|
return duration_cast<std::chrono::duration<double>>(duration).count();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
std::unique_ptr<Application> Application::create() {
|
|
|
|
|
|
return std::unique_ptr<Application>(new Application());
|
2026-02-27 20:46:16 +08:00
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
Application::Application() = default;
|
|
|
|
|
|
|
|
|
|
|
|
Application::~Application() {
|
|
|
|
|
|
shutdown();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Application::Application(Application&&) noexcept = default;
|
|
|
|
|
|
Application& Application::operator=(Application&&) noexcept = default;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
bool Application::init(const AppConfig &config) {
|
|
|
|
|
|
if (initialized_) {
|
|
|
|
|
|
E2D_LOG_WARN("Application already initialized");
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
config_ = config;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
|
|
|
|
|
#ifdef __SWITCH__
|
2026-02-27 23:08:49 +08:00
|
|
|
|
Result rc;
|
|
|
|
|
|
rc = romfsInit();
|
|
|
|
|
|
if (R_SUCCEEDED(rc)) {
|
|
|
|
|
|
E2D_LOG_INFO("RomFS initialized successfully");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rc = socketInitializeDefault();
|
|
|
|
|
|
if (R_FAILED(rc)) {
|
|
|
|
|
|
E2D_LOG_WARN("socketInitializeDefault failed");
|
|
|
|
|
|
}
|
2026-02-27 20:46:16 +08:00
|
|
|
|
#endif
|
2026-02-27 22:59:17 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (!Sdl2::initAll()) {
|
|
|
|
|
|
E2D_LOG_ERROR("Failed to initialize SDL2");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
// 创建引擎上下文
|
|
|
|
|
|
context_ = Context::create();
|
|
|
|
|
|
if (!context_) {
|
|
|
|
|
|
E2D_LOG_ERROR("Failed to create context");
|
2026-02-27 23:08:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
// 初始化引擎
|
|
|
|
|
|
if (!context_->init()) {
|
|
|
|
|
|
E2D_LOG_ERROR("Failed to initialize context");
|
2026-02-27 23:08:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 22:30:48 +08:00
|
|
|
|
// 创建窗口模块
|
|
|
|
|
|
windowModule_ = std::make_unique<WindowModule>();
|
|
|
|
|
|
WindowCfg wcfg;
|
|
|
|
|
|
wcfg.title = config_.title;
|
|
|
|
|
|
wcfg.width = config_.width;
|
|
|
|
|
|
wcfg.height = config_.height;
|
|
|
|
|
|
wcfg.fullscreen = config_.fullscreen;
|
|
|
|
|
|
wcfg.resizable = config_.resizable;
|
|
|
|
|
|
wcfg.vsync = config_.vsync;
|
|
|
|
|
|
wcfg.glMajor = config_.glMajor;
|
|
|
|
|
|
wcfg.glMinor = config_.glMinor;
|
|
|
|
|
|
|
|
|
|
|
|
if (!windowModule_->create(wcfg)) {
|
|
|
|
|
|
E2D_LOG_ERROR("Failed to create window");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
windowModule_->setVisible(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建输入模块
|
|
|
|
|
|
inputModule_ = std::make_unique<InputModule>();
|
|
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
initialized_ = true;
|
|
|
|
|
|
running_ = true;
|
|
|
|
|
|
|
|
|
|
|
|
events::OnInit::emit();
|
|
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Application initialized successfully");
|
|
|
|
|
|
E2D_LOG_INFO("Window: {}x{}, Fullscreen: {}, VSync: {}", config_.width,
|
|
|
|
|
|
config_.height, config_.fullscreen, config_.vsync);
|
|
|
|
|
|
return true;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::shutdown() {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (!initialized_)
|
|
|
|
|
|
return;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
events::OnShutdown::emit();
|
2026-02-27 22:59:17 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
E2D_LOG_INFO("Shutting down application...");
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-28 22:30:48 +08:00
|
|
|
|
// 智能指针自动销毁窗口和输入模块
|
|
|
|
|
|
inputModule_.reset();
|
|
|
|
|
|
windowModule_.reset();
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭上下文
|
2026-02-28 20:56:11 +08:00
|
|
|
|
context_.reset();
|
|
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
Sdl2::shutdown();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
|
|
|
|
|
#ifdef __SWITCH__
|
2026-02-27 23:08:49 +08:00
|
|
|
|
romfsExit();
|
|
|
|
|
|
socketExit();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
#endif
|
2026-02-27 20:46:16 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
initialized_ = false;
|
|
|
|
|
|
running_ = false;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
E2D_LOG_INFO("Application shutdown complete");
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::run() {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (!initialized_) {
|
|
|
|
|
|
E2D_LOG_ERROR("Application not initialized");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
lastFrameTime_ = getTimeSeconds();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
while (running_) {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
mainLoop();
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::quit() {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
shouldQuit_ = true;
|
|
|
|
|
|
running_ = false;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::pause() {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (!paused_) {
|
|
|
|
|
|
paused_ = true;
|
|
|
|
|
|
events::OnPause::emit();
|
|
|
|
|
|
E2D_LOG_INFO("Application paused");
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::resume() {
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (paused_) {
|
|
|
|
|
|
paused_ = false;
|
|
|
|
|
|
events::OnResume::emit();
|
2026-02-28 20:56:11 +08:00
|
|
|
|
lastFrameTime_ = getTimeSeconds();
|
2026-02-27 23:08:49 +08:00
|
|
|
|
E2D_LOG_INFO("Application resumed");
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::mainLoop() {
|
2026-02-28 22:30:48 +08:00
|
|
|
|
// 处理窗口事件
|
|
|
|
|
|
if (windowModule_) {
|
|
|
|
|
|
if (!windowModule_->pollEvents()) {
|
|
|
|
|
|
// 窗口关闭事件
|
|
|
|
|
|
quit();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 23:08:49 +08:00
|
|
|
|
double currentTime = getTimeSeconds();
|
|
|
|
|
|
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
|
|
|
|
|
lastFrameTime_ = currentTime;
|
|
|
|
|
|
|
|
|
|
|
|
totalTime_ += deltaTime_;
|
|
|
|
|
|
|
|
|
|
|
|
frameCount_++;
|
|
|
|
|
|
fpsTimer_ += deltaTime_;
|
|
|
|
|
|
if (fpsTimer_ >= 1.0f) {
|
|
|
|
|
|
currentFps_ = frameCount_;
|
|
|
|
|
|
frameCount_ = 0;
|
|
|
|
|
|
fpsTimer_ -= 1.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!paused_) {
|
|
|
|
|
|
update();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 22:30:48 +08:00
|
|
|
|
// 交换缓冲区
|
|
|
|
|
|
if (windowModule_) {
|
|
|
|
|
|
windowModule_->swapBuffers();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 20:56:11 +08:00
|
|
|
|
// 帧率限制
|
2026-02-27 23:08:49 +08:00
|
|
|
|
if (!config_.vsync && config_.fpsLimit > 0) {
|
|
|
|
|
|
double frameEndTime = getTimeSeconds();
|
|
|
|
|
|
double frameTime = frameEndTime - currentTime;
|
|
|
|
|
|
double target = 1.0 / static_cast<double>(config_.fpsLimit);
|
|
|
|
|
|
if (frameTime < target) {
|
|
|
|
|
|
std::this_thread::sleep_for(
|
|
|
|
|
|
std::chrono::duration<double>(target - frameTime));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Application::update() {
|
2026-02-28 20:56:11 +08:00
|
|
|
|
// 通过上下文更新引擎
|
|
|
|
|
|
if (context_) {
|
|
|
|
|
|
context_->tick(deltaTime_);
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 22:30:48 +08:00
|
|
|
|
int32 Application::getWindowWidth() const {
|
|
|
|
|
|
if (windowModule_) {
|
|
|
|
|
|
Size size = windowModule_->getSize();
|
|
|
|
|
|
return static_cast<int32>(size.w);
|
|
|
|
|
|
}
|
|
|
|
|
|
return config_.width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32 Application::getWindowHeight() const {
|
|
|
|
|
|
if (windowModule_) {
|
|
|
|
|
|
Size size = windowModule_->getSize();
|
|
|
|
|
|
return static_cast<int32>(size.h);
|
|
|
|
|
|
}
|
|
|
|
|
|
return config_.height;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* Application::getWindowTitle() const {
|
|
|
|
|
|
return config_.title.c_str();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 19:40:26 +08:00
|
|
|
|
} // namespace extra2d
|