diff --git a/Extra2D/include/extra2d/app/application.h b/Extra2D/include/extra2d/app/application.h index 98c7945..f498ce2 100644 --- a/Extra2D/include/extra2d/app/application.h +++ b/Extra2D/include/extra2d/app/application.h @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include @@ -18,198 +16,58 @@ class EventQueue; class EventDispatcher; class Camera; class ViewportAdapter; +class RenderBackend; -/** - * @brief Application 单例 - 应用主控 - * - * 负责管理应用程序的整个生命周期,包括初始化、主循环、渲染和关闭。 - * 集成了 ConfigManager 和 ModuleRegistry 系统进行配置和模块管理。 - */ class Application { public: - /** - * @brief 获取单例实例 - * @return Application 实例引用 - */ static Application& get(); Application(const Application&) = delete; Application& operator=(const Application&) = delete; - /** - * @brief 使用默认配置初始化 - * @return 初始化成功返回 true - */ bool init(); - - /** - * @brief 使用配置结构初始化 - * @param config 应用配置 - * @return 初始化成功返回 true - */ bool init(const AppConfig& config); - - /** - * @brief 从配置文件初始化 - * @param configPath 配置文件路径(支持 .json 和 .ini) - * @return 初始化成功返回 true - */ bool init(const std::string& configPath); - /** - * @brief 关闭应用程序 - */ void shutdown(); - - /** - * @brief 运行主循环 - */ void run(); - - /** - * @brief 退出应用程序 - */ void quit(); - - /** - * @brief 暂停应用程序 - */ void pause(); - - /** - * @brief 恢复应用程序 - */ void resume(); - /** - * @brief 检查是否暂停 - * @return 暂停状态返回 true - */ bool isPaused() const { return paused_; } - - /** - * @brief 检查是否运行中 - * @return 运行中返回 true - */ bool isRunning() const { return running_; } - /** - * @brief 获取窗口接口 - * @return 窗口接口引用 - */ IWindow& window() { return *window_; } - - /** - * @brief 获取渲染后端 - * @return 渲染后端引用 - */ - RenderBackend& renderer() { return *renderer_; } - - /** - * @brief 获取输入接口 - * @return 输入接口引用 - */ + RenderBackend& renderer(); IInput& input(); - /** - * @brief 获取场景管理器 - * @return 场景管理器引用 - */ SceneManager& scenes(); - - /** - * @brief 获取定时器管理器 - * @return 定时器管理器引用 - */ TimerManager& timers(); - - /** - * @brief 获取事件队列 - * @return 事件队列引用 - */ EventQueue& eventQueue(); - - /** - * @brief 获取事件分发器 - * @return 事件分发器引用 - */ EventDispatcher& eventDispatcher(); - - /** - * @brief 获取相机 - * @return 相机引用 - */ Camera& camera(); - - /** - * @brief 获取视口适配器 - * @return 视口适配器引用 - */ ViewportAdapter& viewportAdapter(); - /** - * @brief 进入场景 - * @param scene 场景指针 - */ void enterScene(Ptr scene); - /** - * @brief 获取帧时间 - * @return 帧时间(秒) - */ float deltaTime() const { return deltaTime_; } - - /** - * @brief 获取总运行时间 - * @return 总运行时间(秒) - */ float totalTime() const { return totalTime_; } - - /** - * @brief 获取当前帧率 - * @return 帧率(FPS) - */ int fps() const { return currentFps_; } - /** - * @brief 获取配置管理器 - * @return 配置管理器引用 - */ ConfigManager& config(); - - /** - * @brief 获取应用配置 - * @return 应用配置常量引用 - */ const AppConfig& getConfig() const; private: Application() = default; ~Application(); - /** - * @brief 内部初始化实现 - * @return 初始化成功返回 true - */ - bool initImpl(); - - /** - * @brief 主循环 - */ + bool initModules(); void mainLoop(); - - /** - * @brief 更新逻辑 - */ void update(); - - /** - * @brief 渲染 - */ void render(); - UniquePtr window_; - UniquePtr renderer_; + IWindow* window_ = nullptr; UniquePtr sceneManager_; UniquePtr timerManager_; UniquePtr eventQueue_; @@ -228,10 +86,6 @@ private: int frameCount_ = 0; float fpsTimer_ = 0.0f; int currentFps_ = 0; - - ModuleId windowModuleId_ = INVALID_MODULE_ID; - ModuleId inputModuleId_ = INVALID_MODULE_ID; - ModuleId renderModuleId_ = INVALID_MODULE_ID; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/config/config_module.h b/Extra2D/include/extra2d/config/config_module.h new file mode 100644 index 0000000..f370689 --- /dev/null +++ b/Extra2D/include/extra2d/config/config_module.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +class ConfigModuleConfig : public IModuleConfig { +public: + std::string configPath; + AppConfig appConfig; + + ModuleInfo getModuleInfo() const override { + ModuleInfo info; + info.id = 0; + info.name = "Config"; + info.version = "1.0.0"; + info.priority = ModulePriority::Core; + info.enabled = true; + return info; + } + + std::string getConfigSectionName() const override { + return "config"; + } + + bool validate() const override { + return true; + } + + void resetToDefaults() override { + configPath.clear(); + appConfig = AppConfig{}; + } + + bool loadFromJson(const void* jsonData) override; + bool saveToJson(void* jsonData) const override; +}; + +class ConfigModuleInitializer : public IModuleInitializer { +public: + ConfigModuleInitializer(); + ~ConfigModuleInitializer() override; + + ModuleId getModuleId() const override { return moduleId_; } + ModulePriority getPriority() const override { return ModulePriority::Core; } + std::vector getDependencies() const override { return {}; } + + bool initialize(const IModuleConfig* config) override; + void shutdown() override; + bool isInitialized() const override { return initialized_; } + + void setModuleId(ModuleId id) { moduleId_ = id; } + void setAppConfig(const AppConfig& config) { appConfig_ = config; } + void setConfigPath(const std::string& path) { configPath_ = path; } + +private: + ModuleId moduleId_ = INVALID_MODULE_ID; + bool initialized_ = false; + AppConfig appConfig_; + std::string configPath_; +}; + +ModuleId get_config_module_id(); +void register_config_module(); + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/config/module_registry.h b/Extra2D/include/extra2d/config/module_registry.h index 3faa371..64d6e32 100644 --- a/Extra2D/include/extra2d/config/module_registry.h +++ b/Extra2D/include/extra2d/config/module_registry.h @@ -10,12 +10,13 @@ namespace extra2d { /** * @brief 模块注册表条目结构体 - * 存储模块的配置和初始化器工厂 + * 存储模块的配置和初始化器 */ struct ModuleEntry { ModuleId id; ///< 模块标识符 UniquePtr config; ///< 模块配置 ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数 + UniquePtr initializer; ///< 初始化器实例 bool initialized = false; ///< 是否已初始化 }; @@ -74,11 +75,11 @@ public: IModuleConfig* getModuleConfigByName(const std::string& name) const; /** - * @brief 创建模块初始化器 + * @brief 获取或创建模块初始化器 * @param id 模块标识符 - * @return 初始化器实例,不存在返回 nullptr + * @return 初始化器指针,不存在返回 nullptr */ - UniquePtr createInitializer(ModuleId id) const; + IModuleInitializer* getInitializer(ModuleId id); /** * @brief 获取所有已注册模块标识符 diff --git a/Extra2D/include/extra2d/event/input_codes.h b/Extra2D/include/extra2d/event/input_codes.h deleted file mode 100644 index eeb4f43..0000000 --- a/Extra2D/include/extra2d/event/input_codes.h +++ /dev/null @@ -1,212 +0,0 @@ -#pragma once - -// SDL2 键码定义 -#include - -namespace extra2d { - -// ============================================================================ -// 键盘按键码 (基于 SDL2) -// ============================================================================ -namespace Key { -enum : int { - Unknown = SDLK_UNKNOWN, - Space = SDLK_SPACE, - Apostrophe = SDLK_QUOTE, - Comma = SDLK_COMMA, - Minus = SDLK_MINUS, - Period = SDLK_PERIOD, - Slash = SDLK_SLASH, - Num0 = SDLK_0, - Num1 = SDLK_1, - Num2 = SDLK_2, - Num3 = SDLK_3, - Num4 = SDLK_4, - Num5 = SDLK_5, - Num6 = SDLK_6, - Num7 = SDLK_7, - Num8 = SDLK_8, - Num9 = SDLK_9, - Semicolon = SDLK_SEMICOLON, - Equal = SDLK_EQUALS, - A = SDLK_a, - B = SDLK_b, - C = SDLK_c, - D = SDLK_d, - E = SDLK_e, - F = SDLK_f, - G = SDLK_g, - H = SDLK_h, - I = SDLK_i, - J = SDLK_j, - K = SDLK_k, - L = SDLK_l, - M = SDLK_m, - N = SDLK_n, - O = SDLK_o, - P = SDLK_p, - Q = SDLK_q, - R = SDLK_r, - S = SDLK_s, - T = SDLK_t, - U = SDLK_u, - V = SDLK_v, - W = SDLK_w, - X = SDLK_x, - Y = SDLK_y, - Z = SDLK_z, - LeftBracket = SDLK_LEFTBRACKET, - Backslash = SDLK_BACKSLASH, - RightBracket = SDLK_RIGHTBRACKET, - GraveAccent = SDLK_BACKQUOTE, - Escape = SDLK_ESCAPE, - Enter = SDLK_RETURN, - Tab = SDLK_TAB, - Backspace = SDLK_BACKSPACE, - Insert = SDLK_INSERT, - Delete = SDLK_DELETE, - Right = SDLK_RIGHT, - Left = SDLK_LEFT, - Down = SDLK_DOWN, - Up = SDLK_UP, - PageUp = SDLK_PAGEUP, - PageDown = SDLK_PAGEDOWN, - Home = SDLK_HOME, - End = SDLK_END, - CapsLock = SDLK_CAPSLOCK, - ScrollLock = SDLK_SCROLLLOCK, - NumLock = SDLK_NUMLOCKCLEAR, - PrintScreen = SDLK_PRINTSCREEN, - Pause = SDLK_PAUSE, - F1 = SDLK_F1, - F2 = SDLK_F2, - F3 = SDLK_F3, - F4 = SDLK_F4, - F5 = SDLK_F5, - F6 = SDLK_F6, - F7 = SDLK_F7, - F8 = SDLK_F8, - F9 = SDLK_F9, - F10 = SDLK_F10, - F11 = SDLK_F11, - F12 = SDLK_F12, - F13 = SDLK_F13, - F14 = SDLK_F14, - F15 = SDLK_F15, - F16 = SDLK_F16, - F17 = SDLK_F17, - F18 = SDLK_F18, - F19 = SDLK_F19, - F20 = SDLK_F20, - F21 = SDLK_F21, - F22 = SDLK_F22, - F23 = SDLK_F23, - F24 = SDLK_F24, - KP0 = SDLK_KP_0, - KP1 = SDLK_KP_1, - KP2 = SDLK_KP_2, - KP3 = SDLK_KP_3, - KP4 = SDLK_KP_4, - KP5 = SDLK_KP_5, - KP6 = SDLK_KP_6, - KP7 = SDLK_KP_7, - KP8 = SDLK_KP_8, - KP9 = SDLK_KP_9, - KPDecimal = SDLK_KP_PERIOD, - KPDivide = SDLK_KP_DIVIDE, - KPMultiply = SDLK_KP_MULTIPLY, - KPSubtract = SDLK_KP_MINUS, - KPAdd = SDLK_KP_PLUS, - KPEnter = SDLK_KP_ENTER, - KPEqual = SDLK_KP_EQUALS, - LeftShift = SDLK_LSHIFT, - LeftControl = SDLK_LCTRL, - LeftAlt = SDLK_LALT, - LeftSuper = SDLK_LGUI, - RightShift = SDLK_RSHIFT, - RightControl = SDLK_RCTRL, - RightAlt = SDLK_RALT, - RightSuper = SDLK_RGUI, - Menu = SDLK_MENU, - Last = SDLK_MENU -}; -} - -// ============================================================================ -// 修饰键 -// ============================================================================ -namespace Mod { -enum : int { - Shift = KMOD_SHIFT, - Control = KMOD_CTRL, - Alt = KMOD_ALT, - Super = KMOD_GUI, - CapsLock = KMOD_CAPS, - NumLock = KMOD_NUM -}; -} - -// ============================================================================ -// 鼠标按键码 -// ============================================================================ -namespace Mouse { -enum : int { - Button1 = 0, - Button2 = 1, - Button3 = 2, - Button4 = 3, - Button5 = 4, - Button6 = 5, - Button7 = 6, - Button8 = 7, - ButtonLast = Button8, - ButtonLeft = Button1, - ButtonRight = Button2, - ButtonMiddle = Button3 -}; -} - -// ============================================================================ -// 游戏手柄按键 -// ============================================================================ -namespace GamepadButton { -enum : int { - A = SDL_CONTROLLER_BUTTON_A, - B = SDL_CONTROLLER_BUTTON_B, - X = SDL_CONTROLLER_BUTTON_X, - Y = SDL_CONTROLLER_BUTTON_Y, - LeftBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - RightBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - Back = SDL_CONTROLLER_BUTTON_BACK, - Start = SDL_CONTROLLER_BUTTON_START, - Guide = SDL_CONTROLLER_BUTTON_GUIDE, - LeftThumb = SDL_CONTROLLER_BUTTON_LEFTSTICK, - RightThumb = SDL_CONTROLLER_BUTTON_RIGHTSTICK, - DPadUp = SDL_CONTROLLER_BUTTON_DPAD_UP, - DPadRight = SDL_CONTROLLER_BUTTON_DPAD_RIGHT, - DPadDown = SDL_CONTROLLER_BUTTON_DPAD_DOWN, - DPadLeft = SDL_CONTROLLER_BUTTON_DPAD_LEFT, - Last = SDL_CONTROLLER_BUTTON_DPAD_LEFT, - Cross = A, - Circle = B, - Square = X, - Triangle = Y -}; -} - -// ============================================================================ -// 游戏手柄轴 -// ============================================================================ -namespace GamepadAxis { -enum : int { - LeftX = SDL_CONTROLLER_AXIS_LEFTX, - LeftY = SDL_CONTROLLER_AXIS_LEFTY, - RightX = SDL_CONTROLLER_AXIS_RIGHTX, - RightY = SDL_CONTROLLER_AXIS_RIGHTY, - LeftTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT, - RightTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT, - Last = SDL_CONTROLLER_AXIS_TRIGGERRIGHT -}; -} - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/render_module.h b/Extra2D/include/extra2d/graphics/render_module.h index 2479909..6be473a 100644 --- a/Extra2D/include/extra2d/graphics/render_module.h +++ b/Extra2D/include/extra2d/graphics/render_module.h @@ -7,10 +7,6 @@ namespace extra2d { -/** - * @brief 渲染模块配置 - * 实现 IModuleConfig 接口 - */ class RenderModuleConfig : public IModuleConfig { public: BackendType backend = BackendType::OpenGL; @@ -20,10 +16,6 @@ public: bool sRGBFramebuffer = false; int spriteBatchSize = 1000; - /** - * @brief 获取模块信息 - * @return 模块信息结构体 - */ ModuleInfo getModuleInfo() const override { ModuleInfo info; info.name = "Render"; @@ -33,115 +25,41 @@ public: return info; } - /** - * @brief 获取配置节名称 - * @return 配置节名称字符串 - */ std::string getConfigSectionName() const override { return "render"; } - /** - * @brief 验证配置有效性 - * @return 如果配置有效返回 true - */ bool validate() const override; - - /** - * @brief 应用平台约束 - * 根据平台特性调整配置 - * @param platform 目标平台类型 - */ void applyPlatformConstraints(PlatformType platform) override; - - /** - * @brief 重置为默认配置 - */ void resetToDefaults() override; - - /** - * @brief 从 JSON 数据加载配置 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ bool loadFromJson(const void* jsonData) override; - - /** - * @brief 保存配置到 JSON 数据 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ bool saveToJson(void* jsonData) const override; }; -/** - * @brief 渲染模块初始化器 - * 实现 IModuleInitializer 接口 - * 依赖窗口模块 - */ class RenderModuleInitializer : public IModuleInitializer { public: - /** - * @brief 构造函数 - */ RenderModuleInitializer(); - - /** - * @brief 析构函数 - */ ~RenderModuleInitializer() override; - /** - * @brief 获取模块标识符 - * @return 模块唯一标识符 - */ ModuleId getModuleId() const override { return moduleId_; } - - /** - * @brief 获取模块优先级 - * @return 模块优先级 - */ ModulePriority getPriority() const override { return ModulePriority::Graphics; } - - /** - * @brief 获取模块依赖列表 - * @return 依赖模块标识符列表 - */ std::vector getDependencies() const override; - /** - * @brief 初始化模块 - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ bool initialize(const IModuleConfig* config) override; - - /** - * @brief 关闭模块 - */ void shutdown() override; - - /** - * @brief 检查模块是否已初始化 - * @return 已初始化返回 true - */ bool isInitialized() const override { return initialized_; } - /** - * @brief 获取渲染器实例 - * @return 渲染后端指针 - */ + void setModuleId(ModuleId id) { moduleId_ = id; } + void setWindow(IWindow* window) { window_ = window; } + RenderBackend* getRenderer() const { return renderer_.get(); } - - /** - * @brief 设置窗口模块标识符 - * @param windowModuleId 窗口模块标识符 - */ - void setWindowModuleId(ModuleId windowModuleId) { windowModuleId_ = windowModuleId; } - + private: ModuleId moduleId_ = INVALID_MODULE_ID; - ModuleId windowModuleId_ = INVALID_MODULE_ID; + IWindow* window_ = nullptr; UniquePtr renderer_; bool initialized_ = false; }; +ModuleId get_render_module_id(); +void register_render_module(); + } // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/platform_init_module.h b/Extra2D/include/extra2d/platform/platform_init_module.h new file mode 100644 index 0000000..26777ce --- /dev/null +++ b/Extra2D/include/extra2d/platform/platform_init_module.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +class PlatformModuleConfig : public IModuleConfig { +public: + PlatformType targetPlatform = PlatformType::Auto; + + ModuleInfo getModuleInfo() const override { + ModuleInfo info; + info.id = 0; + info.name = "Platform"; + info.version = "1.0.0"; + info.priority = ModulePriority::Core; + info.enabled = true; + return info; + } + + std::string getConfigSectionName() const override { + return "platform"; + } + + bool validate() const override { + return true; + } + + void resetToDefaults() override { + targetPlatform = PlatformType::Auto; + } + + bool loadFromJson(const void* jsonData) override; + bool saveToJson(void* jsonData) const override; +}; + +class PlatformModuleInitializer : public IModuleInitializer { +public: + PlatformModuleInitializer(); + ~PlatformModuleInitializer() override; + + ModuleId getModuleId() const override { return moduleId_; } + ModulePriority getPriority() const override { return ModulePriority::Core; } + std::vector getDependencies() const override { return {}; } + + bool initialize(const IModuleConfig* config) override; + void shutdown() override; + bool isInitialized() const override { return initialized_; } + + void setModuleId(ModuleId id) { moduleId_ = id; } + void setPlatform(PlatformType platform) { targetPlatform_ = platform; } + + PlatformType getPlatform() const { return resolvedPlatform_; } + PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); } + +private: + bool initSwitch(); + void shutdownSwitch(); + + ModuleId moduleId_ = INVALID_MODULE_ID; + bool initialized_ = false; + PlatformType targetPlatform_ = PlatformType::Auto; + PlatformType resolvedPlatform_ = PlatformType::Windows; + UniquePtr platformConfig_; +}; + +ModuleId get_platform_module_id(); +void register_platform_module(); + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/window_module.h b/Extra2D/include/extra2d/platform/window_module.h index 62f5e25..9f19871 100644 --- a/Extra2D/include/extra2d/platform/window_module.h +++ b/Extra2D/include/extra2d/platform/window_module.h @@ -4,133 +4,74 @@ #include #include #include -#include +#include namespace extra2d { -/** - * @brief 窗口模块配置 - * 实现 IModuleConfig 接口 - */ class WindowModuleConfig : public IModuleConfig { public: - WindowConfigData windowConfig; std::string backend = "sdl2"; - - /** - * @brief 获取模块信息 - * @return 模块信息结构体 - */ + WindowConfigData windowConfig; + ModuleInfo getModuleInfo() const override { ModuleInfo info; + info.id = 0; info.name = "Window"; info.version = "1.0.0"; - info.priority = ModulePriority::Platform; + info.priority = ModulePriority::Core; info.enabled = true; return info; } - - /** - * @brief 获取配置节名称 - * @return 配置节名称字符串 - */ - std::string getConfigSectionName() const override { return "window"; } - - /** - * @brief 验证配置有效性 - * @return 如果配置有效返回 true - */ - bool validate() const override; - - /** - * @brief 应用平台约束 - * 根据平台特性调整配置 - * @param platform 目标平台类型 - */ - void applyPlatformConstraints(PlatformType platform) override; - - /** - * @brief 重置为默认配置 - */ - void resetToDefaults() override; - - /** - * @brief 从 JSON 数据加载配置 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ + + std::string getConfigSectionName() const override { + return "window"; + } + + bool validate() const override { + return windowConfig.width > 0 && windowConfig.height > 0; + } + + void resetToDefaults() override { + backend = "sdl2"; + windowConfig = WindowConfigData{}; + } + bool loadFromJson(const void* jsonData) override; - - /** - * @brief 保存配置到 JSON 数据 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ bool saveToJson(void* jsonData) const override; }; -/** - * @brief 窗口模块初始化器 - * 实现 IModuleInitializer 接口 - */ class WindowModuleInitializer : public IModuleInitializer { public: - /** - * @brief 构造函数 - */ WindowModuleInitializer(); - - /** - * @brief 析构函数 - */ ~WindowModuleInitializer() override; - - /** - * @brief 获取模块标识符 - * @return 模块唯一标识符 - */ + ModuleId getModuleId() const override { return moduleId_; } - - /** - * @brief 获取模块优先级 - * @return 模块优先级 - */ - ModulePriority getPriority() const override { return ModulePriority::Platform; } - - /** - * @brief 获取模块依赖列表 - * @return 依赖模块标识符列表 - */ + ModulePriority getPriority() const override { return ModulePriority::Core; } std::vector getDependencies() const override { return {}; } - - /** - * @brief 初始化模块 - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ + bool initialize(const IModuleConfig* config) override; - - /** - * @brief 关闭模块 - */ void shutdown() override; - - /** - * @brief 检查模块是否已初始化 - * @return 已初始化返回 true - */ bool isInitialized() const override { return initialized_; } - - /** - * @brief 获取窗口实例 - * @return 窗口接口指针 - */ + + void setModuleId(ModuleId id) { moduleId_ = id; } + void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; } + IWindow* getWindow() const { return window_.get(); } - + private: + bool initBackend(); + bool createWindow(const std::string& backend, const WindowConfigData& config); + void shutdownBackend(); + ModuleId moduleId_ = INVALID_MODULE_ID; - UniquePtr window_; bool initialized_ = false; + bool backendInitialized_ = false; + std::string backend_; + WindowConfigData windowConfig_; + UniquePtr window_; }; +ModuleId get_window_module_id(); +void register_window_module(); + } // namespace extra2d diff --git a/Extra2D/include/extra2d/utils/logger.h b/Extra2D/include/extra2d/utils/logger.h index 970488f..43a91f4 100644 --- a/Extra2D/include/extra2d/utils/logger.h +++ b/Extra2D/include/extra2d/utils/logger.h @@ -6,254 +6,186 @@ #include #include -// SDL2 日志头文件 -#include - namespace extra2d { -// ============================================================================ -// 日志级别枚举 - 映射到 SDL_LogPriority -// ============================================================================ enum class LogLevel { - Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志 - Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志 - Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志 - Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志 - Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志 - Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志 - Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志 (使用 Critical+1 作为关闭标记) + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4, + Fatal = 5, + Off = 6 }; -// ============================================================================ -// 简单的 fmt-style {} 格式化器 -// ============================================================================ namespace detail { -// 将单个参数转为字符串 template inline std::string to_string_arg(const T &value) { - if constexpr (std::is_same_v) { - return value; - } else if constexpr (std::is_same_v || - std::is_same_v) { - return value ? std::string(value) : std::string("(null)"); - } else if constexpr (std::is_same_v) { - return value ? "true" : "false"; - } else if constexpr (std::is_arithmetic_v) { - // 对浮点数使用特殊格式 - if constexpr (std::is_floating_point_v) { - char buf[64]; - snprintf(buf, sizeof(buf), "%.2f", static_cast(value)); - return buf; + if constexpr (std::is_same_v) { + return value; + } else if constexpr (std::is_same_v || + std::is_same_v) { + return value ? std::string(value) : std::string("(null)"); + } else if constexpr (std::is_same_v) { + return value ? "true" : "false"; + } else if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_floating_point_v) { + char buf[64]; + snprintf(buf, sizeof(buf), "%.2f", static_cast(value)); + return buf; + } else { + return std::to_string(value); + } } else { - return std::to_string(value); + std::ostringstream oss; + oss << value; + return oss.str(); } - } else { - std::ostringstream oss; - oss << value; - return oss.str(); - } } -// 格式化基础情况:没有更多参数 inline std::string format_impl(const char *fmt) { - std::string result; - while (*fmt) { - if (*fmt == '{' && *(fmt + 1) == '}') { - result += "{}"; // 无参数可替换,保留原样 - fmt += 2; - } else { - result += *fmt; - ++fmt; + std::string result; + while (*fmt) { + if (*fmt == '{' && *(fmt + 1) == '}') { + result += "{}"; + fmt += 2; + } else { + result += *fmt; + ++fmt; + } } - } - return result; + return result; } -// 格式化递归:替换第一个 {} 并递归处理剩余 template inline std::string format_impl(const char *fmt, const T &first, const Args &...rest) { - std::string result; - while (*fmt) { - if (*fmt == '{') { - // 检查 {:#x} 等格式说明符 - if (*(fmt + 1) == '}') { - result += to_string_arg(first); - fmt += 2; - result += format_impl(fmt, rest...); - return result; - } else if (*(fmt + 1) == ':') { - // 跳过格式说明符直到 } - const char *end = fmt + 2; - while (*end && *end != '}') - ++end; - if (*end == '}') { - // 检查是否是十六进制格式 - std::string spec(fmt + 2, end); - if (spec.find('x') != std::string::npos || - spec.find('X') != std::string::npos) { - if constexpr (std::is_integral_v) { - char buf[32]; - snprintf(buf, sizeof(buf), "0x%x", - static_cast(first)); - result += buf; - } else { - result += to_string_arg(first); - } - } else if (spec.find('f') != std::string::npos || - spec.find('.') != std::string::npos) { - if constexpr (std::is_arithmetic_v) { - // 解析精度 - int precision = 2; - auto dot = spec.find('.'); - if (dot != std::string::npos) { - precision = 0; - for (size_t i = dot + 1; - i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) { - precision = precision * 10 + (spec[i] - '0'); + std::string result; + while (*fmt) { + if (*fmt == '{') { + if (*(fmt + 1) == '}') { + result += to_string_arg(first); + fmt += 2; + result += format_impl(fmt, rest...); + return result; + } else if (*(fmt + 1) == ':') { + const char *end = fmt + 2; + while (*end && *end != '}') + ++end; + if (*end == '}') { + std::string spec(fmt + 2, end); + if (spec.find('x') != std::string::npos || + spec.find('X') != std::string::npos) { + if constexpr (std::is_integral_v) { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%x", + static_cast(first)); + result += buf; + } else { + result += to_string_arg(first); + } + } else if (spec.find('f') != std::string::npos || + spec.find('.') != std::string::npos) { + if constexpr (std::is_arithmetic_v) { + int precision = 2; + auto dot = spec.find('.'); + if (dot != std::string::npos) { + precision = 0; + for (size_t i = dot + 1; + i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) { + precision = precision * 10 + (spec[i] - '0'); + } + } + char fmtbuf[16]; + snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision); + char buf[64]; + snprintf(buf, sizeof(buf), fmtbuf, static_cast(first)); + result += buf; + } else { + result += to_string_arg(first); + } + } else { + result += to_string_arg(first); + } + fmt = end + 1; + result += format_impl(fmt, rest...); + return result; } - } - char fmtbuf[16]; - snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision); - char buf[64]; - snprintf(buf, sizeof(buf), fmtbuf, static_cast(first)); - result += buf; - } else { - result += to_string_arg(first); } - } else { - result += to_string_arg(first); - } - fmt = end + 1; - result += format_impl(fmt, rest...); - return result; } - } + result += *fmt; + ++fmt; } - result += *fmt; - ++fmt; - } - return result; + return result; } } // namespace detail -// 顶层格式化函数 template inline std::string e2d_format(const char *fmt, const Args &...args) { - return detail::format_impl(fmt, args...); + return detail::format_impl(fmt, args...); } -// 无参数版本 inline std::string e2d_format(const char *fmt) { return std::string(fmt); } -// ============================================================================ -// Logger 类 - 使用 SDL2 日志系统 -// ============================================================================ class Logger { public: - /** - * @brief 初始化日志系统 - */ - static void init(); + static void init(); + static void shutdown(); - /** - * @brief 关闭日志系统 - */ - static void shutdown(); + static void setLevel(LogLevel level); + static void setConsoleOutput(bool enable); + static void setFileOutput(const std::string &filename); - /** - * @brief 设置日志级别 - * @param level 日志级别 - */ - static void setLevel(LogLevel level); + static LogLevel getLevel() { return level_; } - /** - * @brief 设置是否输出到控制台 - * @param enable 是否启用 - */ - static void setConsoleOutput(bool enable); + template + static void log(LogLevel level, const char *fmt, const Args &...args) { + if (static_cast(level) < static_cast(level_)) + return; + std::string msg = e2d_format(fmt, args...); + outputLog(level, msg.c_str()); + } - /** - * @brief 设置日志输出到文件 - * @param filename 日志文件名 - */ - static void setFileOutput(const std::string &filename); - - /** - * @brief 获取当前日志级别 - * @return 当前日志级别 - */ - static LogLevel getLevel() { return level_; } - - /** - * @brief 日志记录模板函数 - * @param level 日志级别 - * @param fmt 格式化字符串 - * @param args 可变参数 - */ - template - static void log(LogLevel level, const char *fmt, const Args &...args) { - if (static_cast(level) < static_cast(level_)) - return; - std::string msg = e2d_format(fmt, args...); - SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, - static_cast(level), "[%s] %s", - getLevelString(level), msg.c_str()); - } - - /** - * @brief 日志记录无参数版本 - * @param level 日志级别 - * @param msg 日志消息 - */ - static void log(LogLevel level, const char *msg) { - if (static_cast(level) < static_cast(level_)) - return; - SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, - static_cast(level), "[%s] %s", - getLevelString(level), msg); - } + static void log(LogLevel level, const char *msg) { + if (static_cast(level) < static_cast(level_)) + return; + outputLog(level, msg); + } private: - static LogLevel level_; // 当前日志级别 - static bool initialized_; // 是否已初始化 - static bool consoleOutput_; // 是否输出到控制台 - static bool fileOutput_; // 是否输出到文件 - static std::string logFile_; // 日志文件路径 + static void outputLog(LogLevel level, const char *msg); + static const char *getLevelString(LogLevel level); + static void writeToConsole(LogLevel level, const char *msg); + static void writeToFile(LogLevel level, const char *msg); - /** - * @brief 获取日志级别字符串 - * @param level 日志级别 - * @return 级别字符串 - */ - static const char *getLevelString(LogLevel level); + static LogLevel level_; + static bool initialized_; + static bool consoleOutput_; + static bool fileOutput_; + static std::string logFile_; + static void *logFileHandle_; }; -// ============================================================================ -// 日志宏 -// ============================================================================ - #ifdef E2D_DEBUG #define E2D_LOG_TRACE(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__) #define E2D_LOG_DEBUG(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__) #else #define E2D_LOG_TRACE(...) #define E2D_LOG_DEBUG(...) #endif #define E2D_LOG_INFO(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__) #define E2D_LOG_WARN(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__) #define E2D_LOG_ERROR(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__) #define E2D_LOG_FATAL(...) \ - ::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__) + ::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__) -// 简化的日志宏 #define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__) #define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__) #define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__) diff --git a/Extra2D/include/extra2d/utils/logger_module.h b/Extra2D/include/extra2d/utils/logger_module.h new file mode 100644 index 0000000..59831a7 --- /dev/null +++ b/Extra2D/include/extra2d/utils/logger_module.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +class LoggerModuleConfig : public IModuleConfig { +public: + LogLevel logLevel = LogLevel::Info; + bool consoleOutput = true; + bool fileOutput = false; + std::string logFilePath; + + ModuleInfo getModuleInfo() const override { + ModuleInfo info; + info.id = 0; + info.name = "Logger"; + info.version = "1.0.0"; + info.priority = ModulePriority::Core; + info.enabled = true; + return info; + } + + std::string getConfigSectionName() const override { + return "logger"; + } + + bool validate() const override { + return true; + } + + void resetToDefaults() override { + logLevel = LogLevel::Info; + consoleOutput = true; + fileOutput = false; + logFilePath.clear(); + } + + bool loadFromJson(const void* jsonData) override; + bool saveToJson(void* jsonData) const override; +}; + +class LoggerModuleInitializer : public IModuleInitializer { +public: + LoggerModuleInitializer(); + ~LoggerModuleInitializer() override; + + ModuleId getModuleId() const override { return moduleId_; } + ModulePriority getPriority() const override { return ModulePriority::Core; } + std::vector getDependencies() const override { return {}; } + + bool initialize(const IModuleConfig* config) override; + void shutdown() override; + bool isInitialized() const override { return initialized_; } + + void setModuleId(ModuleId id) { moduleId_ = id; } + +private: + ModuleId moduleId_ = INVALID_MODULE_ID; + bool initialized_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index 86abfbe..1db115c 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,39 +1,23 @@ #include -#include +#include #include #include #include #include -#include +#include #include #include #include -#include +#include +#include #include -#include #include #include #include -#include - -#ifdef __SWITCH__ -#include -#endif - -#ifdef E2D_BACKEND_SDL2 -namespace extra2d { -void initSDL2Backend(); -} -#endif - namespace extra2d { -/** - * @brief 获取当前时间(秒) - * @return 当前时间戳(秒) - */ static double getTimeSeconds() { #ifdef __SWITCH__ struct timespec ts; @@ -67,170 +51,97 @@ bool Application::init(const AppConfig& config) { return true; } - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) { - E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError()); - return false; + register_config_module(); + register_platform_module(); + register_window_module(); + register_render_module(); + + auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id()); + if (configInit) { + auto* cfgInit = dynamic_cast(configInit); + if (cfgInit) { + cfgInit->setAppConfig(config); + } } - Logger::init(); - - E2D_LOG_INFO("Initializing application with config..."); - - if (!ConfigManager::instance().initialize()) { - E2D_LOG_ERROR("Failed to initialize ConfigManager"); - return false; - } - - ConfigManager::instance().setAppConfig(config); - - return initImpl(); + return initModules(); } bool Application::init(const std::string& configPath) { if (initialized_) { - E2D_LOG_WARN("Application already initialized"); return true; } - E2D_LOG_INFO("Initializing application from config file: {}", configPath); + register_config_module(); + register_platform_module(); + register_window_module(); + register_render_module(); - if (!ConfigManager::instance().initialize(configPath)) { - E2D_LOG_WARN("Failed to load config from file, using defaults"); - if (!ConfigManager::instance().initialize()) { - E2D_LOG_ERROR("Failed to initialize ConfigManager"); - return false; + auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id()); + if (configInit) { + auto* cfgInit = dynamic_cast(configInit); + if (cfgInit) { + cfgInit->setConfigPath(configPath); } } - return initImpl(); + return initModules(); } -bool Application::initImpl() { -#ifdef E2D_BACKEND_SDL2 - initSDL2Backend(); -#endif - - auto& configMgr = ConfigManager::instance(); - AppConfig& appConfig = configMgr.appConfig(); - - PlatformType platform = appConfig.targetPlatform; - if (platform == PlatformType::Auto) { -#ifdef __SWITCH__ - platform = PlatformType::Switch; -#else -#ifdef _WIN32 - platform = PlatformType::Windows; -#elif defined(__linux__) - platform = PlatformType::Linux; -#elif defined(__APPLE__) - platform = PlatformType::macOS; -#else - platform = PlatformType::Windows; -#endif -#endif - } - - E2D_LOG_INFO("Target platform: {} ({})", getPlatformTypeName(platform), - static_cast(platform)); - - UniquePtr platformConfig = createPlatformConfig(platform); - if (!platformConfig) { - E2D_LOG_ERROR("Failed to create platform config"); - return false; - } - - appConfig.applyPlatformConstraints(*platformConfig); - - const auto& capabilities = platformConfig->capabilities(); - E2D_LOG_INFO("Platform capabilities: windowed={}, fullscreen={}, cursor={}, DPI={}", - capabilities.supportsWindowed, capabilities.supportsFullscreen, - capabilities.supportsCursor, capabilities.supportsDPIAwareness); - - if (platform == PlatformType::Switch) { -#ifdef __SWITCH__ - Result rc; - rc = romfsInit(); - if (R_SUCCEEDED(rc)) { - E2D_LOG_INFO("RomFS initialized successfully"); - } else { - E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc); - } - - rc = socketInitializeDefault(); - if (R_FAILED(rc)) { - E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available"); - } -#endif - } - +bool Application::initModules() { auto initOrder = ModuleRegistry::instance().getInitializationOrder(); - E2D_LOG_INFO("Initializing {} registered modules...", initOrder.size()); - + for (ModuleId moduleId : initOrder) { - auto initializer = ModuleRegistry::instance().createInitializer(moduleId); + auto* initializer = ModuleRegistry::instance().getInitializer(moduleId); if (!initializer) { continue; } auto* moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId); if (!moduleConfig) { - E2D_LOG_WARN("Module {} has no config, skipping", moduleId); continue; } auto info = moduleConfig->getModuleInfo(); if (!info.enabled) { - E2D_LOG_INFO("Module '{}' is disabled, skipping", info.name); continue; } - E2D_LOG_INFO("Initializing module '{}' (priority: {})", - info.name, static_cast(info.priority)); + if (info.name == "Render") { + continue; + } if (!initializer->initialize(moduleConfig)) { - E2D_LOG_ERROR("Failed to initialize module '{}'", info.name); - } - } - - std::string backend = "sdl2"; -#ifdef __SWITCH__ - backend = "switch"; -#endif - - if (!BackendFactory::has(backend)) { - E2D_LOG_ERROR("Backend '{}' not available", backend); - auto backends = BackendFactory::backends(); - if (backends.empty()) { - E2D_LOG_ERROR("No backends registered!"); return false; } - backend = backends[0]; - E2D_LOG_WARN("Using fallback backend: {}", backend); } - window_ = BackendFactory::createWindow(backend); + auto* windowInit = ModuleRegistry::instance().getInitializer(get_window_module_id()); + if (!windowInit || !windowInit->isInitialized()) { + return false; + } + + auto* windowModule = dynamic_cast(windowInit); + if (!windowModule) { + return false; + } + + window_ = windowModule->getWindow(); if (!window_) { - E2D_LOG_ERROR("Failed to create window for backend: {}", backend); return false; } - WindowConfigData winConfig = appConfig.window; - - if (platform == PlatformType::Switch) { - winConfig.mode = WindowMode::Fullscreen; - winConfig.resizable = false; - } - - if (!window_->create(winConfig)) { - E2D_LOG_ERROR("Failed to create window"); - return false; - } - - renderer_ = RenderBackend::create(appConfig.render.backend); - if (!renderer_ || !renderer_->init(window_.get())) { - E2D_LOG_ERROR("Failed to initialize renderer"); - window_->destroy(); - return false; + auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id()); + if (renderInit) { + auto* renderModule = dynamic_cast(renderInit); + if (renderModule) { + renderModule->setWindow(window_); + + auto* renderConfig = ModuleRegistry::instance().getModuleConfig(get_render_module_id()); + if (renderConfig && !renderInit->initialize(renderConfig)) { + return false; + } + } } sceneManager_ = makeUnique(); @@ -242,13 +153,12 @@ bool Application::initImpl() { viewportAdapter_ = makeUnique(); ViewportConfig vpConfig; - vpConfig.logicWidth = static_cast(appConfig.window.width); - vpConfig.logicHeight = static_cast(appConfig.window.height); + vpConfig.logicWidth = static_cast(window_->width()); + vpConfig.logicHeight = static_cast(window_->height()); vpConfig.mode = ViewportMode::AspectRatio; viewportAdapter_->setConfig(vpConfig); camera_->setViewportAdapter(viewportAdapter_.get()); - viewportAdapter_->update(window_->width(), window_->height()); window_->onResize([this](int width, int height) { @@ -272,12 +182,6 @@ bool Application::initImpl() { initialized_ = true; running_ = true; - E2D_LOG_INFO("Application initialized successfully"); - E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height()); - E2D_LOG_INFO(" Backend: {}", backend); - E2D_LOG_INFO(" VSync: {}", appConfig.render.vsync); - E2D_LOG_INFO(" Target FPS: {}", appConfig.render.targetFPS); - return true; } @@ -285,8 +189,6 @@ void Application::shutdown() { if (!initialized_) return; - E2D_LOG_INFO("Shutting down application..."); - VRAMMgr::get().printStats(); if (sceneManager_) { @@ -301,59 +203,24 @@ void Application::shutdown() { eventQueue_.reset(); eventDispatcher_.reset(); - if (renderer_) { - renderer_->shutdown(); - renderer_.reset(); - } + window_ = nullptr; - if (window_) { - window_->destroy(); - window_.reset(); - } - - auto modules = ModuleRegistry::instance().getAllModules(); auto initOrder = ModuleRegistry::instance().getInitializationOrder(); for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) { ModuleId moduleId = *it; - auto initializer = ModuleRegistry::instance().createInitializer(moduleId); + auto* initializer = ModuleRegistry::instance().getInitializer(moduleId); if (initializer && initializer->isInitialized()) { - auto* moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId); - if (moduleConfig) { - auto info = moduleConfig->getModuleInfo(); - E2D_LOG_INFO("Shutting down module '{}'", info.name); - } initializer->shutdown(); } } - PlatformType platform = ConfigManager::instance().appConfig().targetPlatform; - if (platform == PlatformType::Auto) { -#ifdef __SWITCH__ - platform = PlatformType::Switch; -#else - platform = PlatformType::Windows; -#endif - } - - if (platform == PlatformType::Switch) { -#ifdef __SWITCH__ - romfsExit(); - socketExit(); -#endif - } - - ConfigManager::instance().shutdown(); - initialized_ = false; running_ = false; - - E2D_LOG_INFO("Application shutdown complete"); } void Application::run() { if (!initialized_) { - E2D_LOG_ERROR("Application not initialized"); return; } @@ -372,7 +239,6 @@ void Application::quit() { void Application::pause() { if (!paused_) { paused_ = true; - E2D_LOG_INFO("Application paused"); } } @@ -380,7 +246,6 @@ void Application::resume() { if (paused_) { paused_ = false; lastFrameTime_ = getTimeSeconds(); - E2D_LOG_INFO("Application resumed"); } } @@ -436,24 +301,30 @@ void Application::update() { } void Application::render() { - if (!renderer_) { - E2D_LOG_ERROR("Render failed: renderer is null"); + auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id()); + RenderBackend* renderer = nullptr; + if (renderInit) { + auto* renderModule = dynamic_cast(renderInit); + if (renderModule) { + renderer = renderModule->getRenderer(); + } + } + + if (!renderer) { return; } if (viewportAdapter_) { const auto& vp = viewportAdapter_->getViewport(); - renderer_->setViewport( + renderer->setViewport( static_cast(vp.origin.x), static_cast(vp.origin.y), static_cast(vp.size.width), static_cast(vp.size.height)); } else { - renderer_->setViewport(0, 0, window_->width(), window_->height()); + renderer->setViewport(0, 0, window_->width(), window_->height()); } if (sceneManager_) { - sceneManager_->render(*renderer_); - } else { - E2D_LOG_WARN("Render: sceneManager is null"); + sceneManager_->render(*renderer); } window_->swap(); @@ -463,6 +334,21 @@ IInput& Application::input() { return *window_->input(); } +RenderBackend& Application::renderer() { + auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id()); + if (renderInit) { + auto* renderModule = dynamic_cast(renderInit); + if (renderModule && renderModule->getRenderer()) { + return *renderModule->getRenderer(); + } + } + static RenderBackend* dummy = nullptr; + if (!dummy) { + dummy = RenderBackend::create(BackendType::OpenGL).release(); + } + return *dummy; +} + SceneManager& Application::scenes() { return *sceneManager_; } diff --git a/Extra2D/src/config/config_module.cpp b/Extra2D/src/config/config_module.cpp new file mode 100644 index 0000000..ea33fa7 --- /dev/null +++ b/Extra2D/src/config/config_module.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace extra2d { + +static ModuleId s_configModuleId = INVALID_MODULE_ID; + +ModuleId get_config_module_id() { + return s_configModuleId; +} + +bool ConfigModuleConfig::loadFromJson(const void* jsonData) { + if (!jsonData) return false; + + try { + const json& j = *static_cast(jsonData); + + if (j.contains("configPath")) { + configPath = j["configPath"].get(); + } + + return true; + } catch (...) { + return false; + } +} + +bool ConfigModuleConfig::saveToJson(void* jsonData) const { + if (!jsonData) return false; + + try { + json& j = *static_cast(jsonData); + j["configPath"] = configPath; + return true; + } catch (...) { + return false; + } +} + +ConfigModuleInitializer::ConfigModuleInitializer() + : moduleId_(INVALID_MODULE_ID) + , initialized_(false) { +} + +ConfigModuleInitializer::~ConfigModuleInitializer() { + if (initialized_) { + shutdown(); + } +} + +bool ConfigModuleInitializer::initialize(const IModuleConfig* config) { + if (initialized_) return true; + + const ConfigModuleConfig* configModule = dynamic_cast(config); + + if (!configPath_.empty()) { + if (!ConfigManager::instance().initialize(configPath_)) { + if (!ConfigManager::instance().initialize()) { + return false; + } + } + } else { + if (!ConfigManager::instance().initialize()) { + return false; + } + } + + if (configModule && !configModule->appConfig.appName.empty()) { + ConfigManager::instance().setAppConfig(configModule->appConfig); + } else if (!appConfig_.appName.empty()) { + ConfigManager::instance().setAppConfig(appConfig_); + } + + initialized_ = true; + E2D_LOG_INFO("Config module initialized"); + return true; +} + +void ConfigModuleInitializer::shutdown() { + if (!initialized_) return; + + E2D_LOG_INFO("Config module shutting down"); + ConfigManager::instance().shutdown(); + initialized_ = false; +} + +void register_config_module() { + if (s_configModuleId != INVALID_MODULE_ID) return; + + s_configModuleId = ModuleRegistry::instance().registerModule( + makeUnique(), + []() -> UniquePtr { + auto initializer = makeUnique(); + initializer->setModuleId(s_configModuleId); + return initializer; + } + ); +} + +namespace { + struct ConfigModuleAutoRegister { + ConfigModuleAutoRegister() { + register_config_module(); + } + }; + + static ConfigModuleAutoRegister s_autoRegister; +} + +} // namespace extra2d diff --git a/Extra2D/src/config/module_registry.cpp b/Extra2D/src/config/module_registry.cpp index acbc3a4..8c3ce77 100644 --- a/Extra2D/src/config/module_registry.cpp +++ b/Extra2D/src/config/module_registry.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include namespace extra2d { @@ -26,7 +26,7 @@ ModuleId ModuleRegistry::registerModule( ModuleInitializerFactory initializerFactory ) { if (!config) { - E2D_LOG_ERROR("Cannot register null module config"); + std::fprintf(stderr, "[ERROR] Cannot register null module config\n"); return INVALID_MODULE_ID; } @@ -35,7 +35,7 @@ ModuleId ModuleRegistry::registerModule( ModuleInfo info = config->getModuleInfo(); if (nameToId_.find(info.name) != nameToId_.end()) { - E2D_LOG_ERROR("Module '{}' already registered", info.name); + std::fprintf(stderr, "[ERROR] Module '%s' already registered\n", info.name.c_str()); return INVALID_MODULE_ID; } @@ -50,7 +50,6 @@ ModuleId ModuleRegistry::registerModule( modules_[id] = std::move(entry); nameToId_[info.name] = id; - E2D_LOG_INFO("Registered module '{}' with id {}", info.name, id); return id; } @@ -65,7 +64,6 @@ bool ModuleRegistry::unregisterModule(ModuleId id) { auto it = modules_.find(id); if (it == modules_.end()) { - E2D_LOG_WARN("Module with id {} not found for unregistration", id); return false; } @@ -73,7 +71,6 @@ bool ModuleRegistry::unregisterModule(ModuleId id) { nameToId_.erase(info.name); modules_.erase(it); - E2D_LOG_INFO("Unregistered module '{}' (id: {})", info.name, id); return true; } @@ -113,11 +110,11 @@ IModuleConfig* ModuleRegistry::getModuleConfigByName(const std::string& name) co } /** - * @brief 创建模块初始化器 + * @brief 获取或创建模块初始化器 * @param id 模块标识符 - * @return 初始化器实例,不存在返回 nullptr + * @return 初始化器指针,不存在返回 nullptr */ -UniquePtr ModuleRegistry::createInitializer(ModuleId id) const { +IModuleInitializer* ModuleRegistry::getInitializer(ModuleId id) { std::lock_guard lock(mutex_); auto it = modules_.find(id); @@ -125,7 +122,11 @@ UniquePtr ModuleRegistry::createInitializer(ModuleId id) con return nullptr; } - return it->second.initializerFactory(); + if (!it->second.initializer) { + it->second.initializer = it->second.initializerFactory(); + } + + return it->second.initializer.get(); } /** @@ -197,8 +198,6 @@ void ModuleRegistry::clear() { modules_.clear(); nameToId_.clear(); nextId_ = 1; - - E2D_LOG_INFO("Module registry cleared"); } /** diff --git a/Extra2D/src/graphics/render_module.cpp b/Extra2D/src/graphics/render_module.cpp index 7ac0195..c1ab7c0 100644 --- a/Extra2D/src/graphics/render_module.cpp +++ b/Extra2D/src/graphics/render_module.cpp @@ -9,85 +9,47 @@ using json = nlohmann::json; namespace extra2d { -// ============================================================================ -// RenderModuleConfig 实现 -// ============================================================================ +static ModuleId s_renderModuleId = INVALID_MODULE_ID; + +ModuleId get_render_module_id() { + return s_renderModuleId; +} -/** - * @brief 验证渲染配置有效性 - * - * 检查渲染配置的各项参数是否在有效范围内: - * - 目标帧率应在 1-240 之间 - * - 多重采样数应为 0、2、4、8 或 16 - * - 精灵批处理大小应大于 0 - * - * @return 如果配置有效返回 true - */ bool RenderModuleConfig::validate() const { if (targetFPS < 1 || targetFPS > 240) { - E2D_LOG_ERROR("Invalid target FPS: {}, must be between 1 and 240", targetFPS); return false; } if (multisamples != 0 && multisamples != 2 && multisamples != 4 && multisamples != 8 && multisamples != 16) { - E2D_LOG_ERROR("Invalid multisample count: {}, must be 0, 2, 4, 8 or 16", multisamples); return false; } if (spriteBatchSize <= 0) { - E2D_LOG_ERROR("Invalid sprite batch size: {}, must be greater than 0", spriteBatchSize); return false; } return true; } -/** - * @brief 应用平台约束 - * - * 根据不同平台的特性调整渲染配置: - * - Switch 平台限制 MSAA 最大为 4,禁用 sRGB 帧缓冲 - * - 其他平台保持用户配置 - * - * @param platform 目标平台类型 - */ void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) { switch (platform) { case PlatformType::Switch: if (multisamples > 4) { - E2D_LOG_WARN("Switch platform limits MSAA to 4x, reducing from {}", multisamples); multisamples = 4; } if (sRGBFramebuffer) { - E2D_LOG_WARN("Switch platform does not support sRGB framebuffer, disabling"); sRGBFramebuffer = false; } if (targetFPS > 60) { - E2D_LOG_WARN("Switch platform target FPS capped at 60"); targetFPS = 60; } break; - - case PlatformType::Windows: - case PlatformType::Linux: - case PlatformType::macOS: default: break; } } -/** - * @brief 重置为默认配置 - * - * 将所有配置项恢复为默认值: - * - 后端类型:OpenGL - * - 垂直同步:启用 - * - 目标帧率:60 - * - 多重采样:禁用 - * - sRGB 帧缓冲:禁用 - * - 精灵批处理大小:1000 - */ void RenderModuleConfig::resetToDefaults() { backend = BackendType::OpenGL; vsync = true; @@ -97,19 +59,8 @@ void RenderModuleConfig::resetToDefaults() { spriteBatchSize = 1000; } -/** - * @brief 从 JSON 数据加载配置 - * - * 从 JSON 对象中解析渲染配置参数 - * - * @param jsonData JSON 数据指针(nlohmann::json 对象指针) - * @return 加载成功返回 true - */ bool RenderModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) { - E2D_LOG_ERROR("Null JSON data provided"); - return false; - } + if (!jsonData) return false; try { const json& j = *static_cast(jsonData); @@ -118,9 +69,6 @@ bool RenderModuleConfig::loadFromJson(const void* jsonData) { std::string backendStr = j["backend"].get(); if (backendStr == "opengl") { backend = BackendType::OpenGL; - } else { - E2D_LOG_WARN("Unknown backend type: {}, defaulting to OpenGL", backendStr); - backend = BackendType::OpenGL; } } @@ -144,124 +92,57 @@ bool RenderModuleConfig::loadFromJson(const void* jsonData) { spriteBatchSize = j["spriteBatchSize"].get(); } - E2D_LOG_INFO("Render config loaded from JSON"); return true; - } catch (const json::exception& e) { - E2D_LOG_ERROR("Failed to parse render config from JSON: {}", e.what()); + } catch (...) { return false; } } -/** - * @brief 保存配置到 JSON 数据 - * - * 将当前配置序列化到 JSON 对象 - * - * @param jsonData JSON 数据指针(nlohmann::json 对象指针) - * @return 保存成功返回 true - */ bool RenderModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) { - E2D_LOG_ERROR("Null JSON data provided"); - return false; - } + if (!jsonData) return false; try { json& j = *static_cast(jsonData); - - std::string backendStr = "opengl"; - switch (backend) { - case BackendType::OpenGL: - backendStr = "opengl"; - break; - default: - backendStr = "opengl"; - break; - } - - j["backend"] = backendStr; + j["backend"] = "opengl"; j["vsync"] = vsync; j["targetFPS"] = targetFPS; j["multisamples"] = multisamples; j["sRGBFramebuffer"] = sRGBFramebuffer; j["spriteBatchSize"] = spriteBatchSize; - - E2D_LOG_INFO("Render config saved to JSON"); return true; - } catch (const json::exception& e) { - E2D_LOG_ERROR("Failed to save render config to JSON: {}", e.what()); + } catch (...) { return false; } } -// ============================================================================ -// RenderModuleInitializer 实现 -// ============================================================================ - -/** - * @brief 构造函数 - * - * 初始化渲染模块初始化器的成员变量 - */ RenderModuleInitializer::RenderModuleInitializer() : moduleId_(INVALID_MODULE_ID) - , windowModuleId_(INVALID_MODULE_ID) - , renderer_(nullptr) + , window_(nullptr) , initialized_(false) { } -/** - * @brief 析构函数 - * - * 确保在销毁时关闭模块 - */ RenderModuleInitializer::~RenderModuleInitializer() { if (initialized_) { shutdown(); } } -/** - * @brief 获取模块依赖列表 - * - * 返回渲染模块依赖的窗口模块标识符列表 - * - * @return 依赖模块标识符列表 - */ std::vector RenderModuleInitializer::getDependencies() const { - if (windowModuleId_ != INVALID_MODULE_ID) { - return {windowModuleId_}; - } return {}; } -/** - * @brief 初始化模块 - * - * 根据配置创建渲染后端实例并初始化渲染器 - * - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ bool RenderModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) { - E2D_LOG_WARN("Render module already initialized"); - return true; - } + if (initialized_) return true; - if (!config) { - E2D_LOG_ERROR("Null config provided for render module initialization"); - return false; - } + if (!config) return false; const RenderModuleConfig* renderConfig = dynamic_cast(config); - if (!renderConfig) { - E2D_LOG_ERROR("Invalid config type for render module"); - return false; - } + if (!renderConfig) return false; - if (!renderConfig->validate()) { - E2D_LOG_ERROR("Invalid render module configuration"); + if (!renderConfig->validate()) return false; + + if (!window_) { + E2D_LOG_ERROR("Render module requires window to be set"); return false; } @@ -271,35 +152,19 @@ bool RenderModuleInitializer::initialize(const IModuleConfig* config) { return false; } - IWindow* window = nullptr; - if (windowModuleId_ != INVALID_MODULE_ID) { - ModuleRegistry& registry = ModuleRegistry::instance(); - IModuleConfig* windowConfig = registry.getModuleConfig(windowModuleId_); - if (windowConfig) { - E2D_LOG_INFO("Render module found window module dependency"); - } + if (!renderer_->init(window_)) { + E2D_LOG_ERROR("Failed to initialize renderer"); + renderer_.reset(); + return false; } - E2D_LOG_INFO("Render module initialized successfully"); - E2D_LOG_INFO(" Backend: {}", renderConfig->backend == BackendType::OpenGL ? "OpenGL" : "Unknown"); - E2D_LOG_INFO(" VSync: {}", renderConfig->vsync ? "enabled" : "disabled"); - E2D_LOG_INFO(" Target FPS: {}", renderConfig->targetFPS); - E2D_LOG_INFO(" Multisamples: {}", renderConfig->multisamples); - E2D_LOG_INFO(" Sprite Batch Size: {}", renderConfig->spriteBatchSize); - initialized_ = true; + E2D_LOG_INFO("Render module initialized"); return true; } -/** - * @brief 关闭模块 - * - * 销毁渲染后端实例并清理资源 - */ void RenderModuleInitializer::shutdown() { - if (!initialized_) { - return; - } + if (!initialized_) return; if (renderer_) { renderer_->shutdown(); @@ -307,7 +172,30 @@ void RenderModuleInitializer::shutdown() { } initialized_ = false; - E2D_LOG_INFO("Render module shutdown complete"); + E2D_LOG_INFO("Render module shutdown"); +} + +void register_render_module() { + if (s_renderModuleId != INVALID_MODULE_ID) return; + + s_renderModuleId = ModuleRegistry::instance().registerModule( + makeUnique(), + []() -> UniquePtr { + auto initializer = makeUnique(); + initializer->setModuleId(s_renderModuleId); + return initializer; + } + ); +} + +namespace { + struct RenderModuleAutoRegister { + RenderModuleAutoRegister() { + register_render_module(); + } + }; + + static RenderModuleAutoRegister s_autoRegister; } } // namespace extra2d diff --git a/Extra2D/src/platform/platform_init_module.cpp b/Extra2D/src/platform/platform_init_module.cpp new file mode 100644 index 0000000..5e9f523 --- /dev/null +++ b/Extra2D/src/platform/platform_init_module.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include + +#ifdef __SWITCH__ +#include +#endif + +using json = nlohmann::json; + +namespace extra2d { + +static ModuleId s_platformModuleId = INVALID_MODULE_ID; + +ModuleId get_platform_module_id() { + return s_platformModuleId; +} + +bool PlatformModuleConfig::loadFromJson(const void* jsonData) { + if (!jsonData) return false; + + try { + const json& j = *static_cast(jsonData); + + if (j.contains("targetPlatform")) { + int platform = j["targetPlatform"].get(); + if (platform >= 0 && platform <= 5) { + targetPlatform = static_cast(platform); + } + } + + return true; + } catch (...) { + return false; + } +} + +bool PlatformModuleConfig::saveToJson(void* jsonData) const { + if (!jsonData) return false; + + try { + json& j = *static_cast(jsonData); + j["targetPlatform"] = static_cast(targetPlatform); + return true; + } catch (...) { + return false; + } +} + +PlatformModuleInitializer::PlatformModuleInitializer() + : moduleId_(INVALID_MODULE_ID) + , initialized_(false) + , targetPlatform_(PlatformType::Auto) + , resolvedPlatform_(PlatformType::Windows) { +} + +PlatformModuleInitializer::~PlatformModuleInitializer() { + if (initialized_) { + shutdown(); + } +} + +bool PlatformModuleInitializer::initialize(const IModuleConfig* config) { + if (initialized_) return true; + + const PlatformModuleConfig* platformConfig = dynamic_cast(config); + if (platformConfig) { + targetPlatform_ = platformConfig->targetPlatform; + } + + resolvedPlatform_ = targetPlatform_; + if (resolvedPlatform_ == PlatformType::Auto) { +#ifdef __SWITCH__ + resolvedPlatform_ = PlatformType::Switch; +#else +#ifdef _WIN32 + resolvedPlatform_ = PlatformType::Windows; +#elif defined(__linux__) + resolvedPlatform_ = PlatformType::Linux; +#elif defined(__APPLE__) + resolvedPlatform_ = PlatformType::macOS; +#else + resolvedPlatform_ = PlatformType::Windows; +#endif +#endif + } + + platformConfig_ = createPlatformConfig(resolvedPlatform_); + if (!platformConfig_) { + E2D_LOG_ERROR("Failed to create platform config"); + return false; + } + + auto& appConfig = ConfigManager::instance().appConfig(); + appConfig.applyPlatformConstraints(*platformConfig_); + + if (resolvedPlatform_ == PlatformType::Switch) { + if (!initSwitch()) { + return false; + } + } + + initialized_ = true; + E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_)); + return true; +} + +bool PlatformModuleInitializer::initSwitch() { +#ifdef __SWITCH__ + Result rc; + rc = romfsInit(); + if (R_SUCCEEDED(rc)) { + E2D_LOG_INFO("RomFS initialized successfully"); + } else { + E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc); + } + + rc = socketInitializeDefault(); + if (R_FAILED(rc)) { + E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available"); + } +#endif + return true; +} + +void PlatformModuleInitializer::shutdown() { + if (!initialized_) return; + + E2D_LOG_INFO("Platform module shutting down"); + + if (resolvedPlatform_ == PlatformType::Switch) { + shutdownSwitch(); + } + + platformConfig_.reset(); + initialized_ = false; +} + +void PlatformModuleInitializer::shutdownSwitch() { +#ifdef __SWITCH__ + romfsExit(); + socketExit(); +#endif +} + +void register_platform_module() { + if (s_platformModuleId != INVALID_MODULE_ID) return; + + s_platformModuleId = ModuleRegistry::instance().registerModule( + makeUnique(), + []() -> UniquePtr { + auto initializer = makeUnique(); + initializer->setModuleId(s_platformModuleId); + return initializer; + } + ); +} + +namespace { + struct PlatformModuleAutoRegister { + PlatformModuleAutoRegister() { + register_platform_module(); + } + }; + + static PlatformModuleAutoRegister s_autoRegister; +} + +} // namespace extra2d diff --git a/Extra2D/src/platform/window_module.cpp b/Extra2D/src/platform/window_module.cpp index c439b46..2e5779e 100644 --- a/Extra2D/src/platform/window_module.cpp +++ b/Extra2D/src/platform/window_module.cpp @@ -1,340 +1,249 @@ #include #include +#include #include #include - #include +#ifdef E2D_BACKEND_SDL2 +#include +#endif + +#ifdef E2D_BACKEND_GLFW +#include +#endif + +#ifdef __SWITCH__ +#include +#endif + using json = nlohmann::json; namespace extra2d { -// ============================================================================ -// WindowModuleConfig 实现 -// ============================================================================ +static ModuleId s_windowModuleId = INVALID_MODULE_ID; -/** - * @brief 验证窗口配置有效性 - * 检查窗口尺寸、标题等配置是否合法 - * @return 如果配置有效返回 true - */ -bool WindowModuleConfig::validate() const { - if (windowConfig.width <= 0) { - E2D_LOG_ERROR("Window width must be positive, got: {}", windowConfig.width); - return false; - } - - if (windowConfig.height <= 0) { - E2D_LOG_ERROR("Window height must be positive, got: {}", windowConfig.height); - return false; - } - - if (windowConfig.title.empty()) { - E2D_LOG_WARN("Window title is empty, using default title"); - } - - if (windowConfig.multisamples < 0) { - E2D_LOG_ERROR("MSAA samples cannot be negative, got: {}", windowConfig.multisamples); - return false; - } - - if (windowConfig.multisamples != 0 && - windowConfig.multisamples != 2 && - windowConfig.multisamples != 4 && - windowConfig.multisamples != 8 && - windowConfig.multisamples != 16) { - E2D_LOG_WARN("MSAA samples should be 0, 2, 4, 8, or 16, got: {}", windowConfig.multisamples); - } - - if (backend.empty()) { - E2D_LOG_ERROR("Backend name cannot be empty"); - return false; - } - - return true; +ModuleId get_window_module_id() { + return s_windowModuleId; } -/** - * @brief 应用平台约束 - * 根据目标平台特性调整窗口配置 - * @param platform 目标平台类型 - */ -void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) { - switch (platform) { - case PlatformType::Switch: - E2D_LOG_INFO("Applying Nintendo Switch platform constraints"); - windowConfig.mode = WindowMode::Fullscreen; - windowConfig.resizable = false; - windowConfig.centered = false; - windowConfig.width = 1920; - windowConfig.height = 1080; - backend = "switch"; - break; - - case PlatformType::Windows: - case PlatformType::Linux: - case PlatformType::macOS: - E2D_LOG_INFO("Applying desktop platform constraints"); - if (windowConfig.width <= 0) { - windowConfig.width = 1280; - } - if (windowConfig.height <= 0) { - windowConfig.height = 720; - } - break; - - case PlatformType::Auto: - default: - E2D_LOG_INFO("Auto-detecting platform constraints"); - break; - } -} - -/** - * @brief 重置为默认配置 - * 将所有配置项恢复为默认值 - */ -void WindowModuleConfig::resetToDefaults() { - windowConfig = WindowConfigData{}; - backend = "sdl2"; - - E2D_LOG_INFO("Window module config reset to defaults"); -} - -/** - * @brief 从 JSON 数据加载配置 - * 解析 JSON 对象并填充配置数据 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ bool WindowModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) { - E2D_LOG_ERROR("JSON data is null"); - return false; - } + if (!jsonData) return false; - const json& obj = *static_cast(jsonData); - - if (!obj.is_object()) { - E2D_LOG_ERROR("JSON data must be an object"); - return false; - } - - if (obj.contains("title") && obj["title"].is_string()) { - windowConfig.title = obj["title"].get(); - } - - if (obj.contains("width") && obj["width"].is_number_integer()) { - windowConfig.width = obj["width"].get(); - } - - if (obj.contains("height") && obj["height"].is_number_integer()) { - windowConfig.height = obj["height"].get(); - } - - if (obj.contains("fullscreen") && obj["fullscreen"].is_boolean()) { - windowConfig.mode = obj["fullscreen"].get() ? WindowMode::Fullscreen : WindowMode::Windowed; - } - - if (obj.contains("mode") && obj["mode"].is_string()) { - std::string modeStr = obj["mode"].get(); - if (modeStr == "fullscreen") { - windowConfig.mode = WindowMode::Fullscreen; - } else if (modeStr == "borderless") { - windowConfig.mode = WindowMode::Borderless; - } else { - windowConfig.mode = WindowMode::Windowed; + try { + const json& j = *static_cast(jsonData); + + if (j.contains("backend")) { + backend = j["backend"].get(); } - } - - if (obj.contains("resizable") && obj["resizable"].is_boolean()) { - windowConfig.resizable = obj["resizable"].get(); - } - - if (obj.contains("vsync") && obj["vsync"].is_boolean()) { - windowConfig.vsync = obj["vsync"].get(); - } - - if (obj.contains("multisamples") && obj["multisamples"].is_number_integer()) { - windowConfig.multisamples = obj["multisamples"].get(); - } - - if (obj.contains("msaaSamples") && obj["msaaSamples"].is_number_integer()) { - windowConfig.multisamples = obj["msaaSamples"].get(); - } - - if (obj.contains("centered") && obj["centered"].is_boolean()) { - windowConfig.centered = obj["centered"].get(); - } - - if (obj.contains("centerWindow") && obj["centerWindow"].is_boolean()) { - windowConfig.centered = obj["centerWindow"].get(); - } - - if (obj.contains("visible") && obj["visible"].is_boolean()) { - windowConfig.visible = obj["visible"].get(); - } - - if (obj.contains("decorated") && obj["decorated"].is_boolean()) { - windowConfig.decorated = obj["decorated"].get(); - } - - if (obj.contains("backend") && obj["backend"].is_string()) { - backend = obj["backend"].get(); - } - - E2D_LOG_INFO("Window module config loaded from JSON"); - return true; -} - -/** - * @brief 保存配置到 JSON 数据 - * 将配置数据序列化为 JSON 对象 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ -bool WindowModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) { - E2D_LOG_ERROR("JSON data pointer is null"); + + if (j.contains("title")) { + windowConfig.title = j["title"].get(); + } + if (j.contains("width")) { + windowConfig.width = j["width"].get(); + } + if (j.contains("height")) { + windowConfig.height = j["height"].get(); + } + if (j.contains("fullscreen")) { + windowConfig.mode = j["fullscreen"].get() ? WindowMode::Fullscreen : WindowMode::Windowed; + } + if (j.contains("vsync")) { + windowConfig.vsync = j["vsync"].get(); + } + if (j.contains("resizable")) { + windowConfig.resizable = j["resizable"].get(); + } + + return true; + } catch (...) { return false; } - - json& obj = *static_cast(jsonData); - - obj["title"] = windowConfig.title; - obj["width"] = windowConfig.width; - obj["height"] = windowConfig.height; - - std::string modeStr = "windowed"; - if (windowConfig.mode == WindowMode::Fullscreen) { - modeStr = "fullscreen"; - } else if (windowConfig.mode == WindowMode::Borderless) { - modeStr = "borderless"; - } - obj["mode"] = modeStr; - - obj["resizable"] = windowConfig.resizable; - obj["vsync"] = windowConfig.vsync; - obj["multisamples"] = windowConfig.multisamples; - obj["centered"] = windowConfig.centered; - obj["visible"] = windowConfig.visible; - obj["decorated"] = windowConfig.decorated; - obj["backend"] = backend; - - E2D_LOG_INFO("Window module config saved to JSON"); - return true; } -// ============================================================================ -// WindowModuleInitializer 实现 -// ============================================================================ +bool WindowModuleConfig::saveToJson(void* jsonData) const { + if (!jsonData) return false; + + try { + json& j = *static_cast(jsonData); + j["backend"] = backend; + j["title"] = windowConfig.title; + j["width"] = windowConfig.width; + j["height"] = windowConfig.height; + j["fullscreen"] = (windowConfig.mode == WindowMode::Fullscreen); + j["vsync"] = windowConfig.vsync; + j["resizable"] = windowConfig.resizable; + return true; + } catch (...) { + return false; + } +} -/** - * @brief 构造函数 - * 初始化窗口模块初始化器 - */ WindowModuleInitializer::WindowModuleInitializer() : moduleId_(INVALID_MODULE_ID) - , window_(nullptr) - , initialized_(false) { - E2D_LOG_DEBUG("WindowModuleInitializer constructed"); + , initialized_(false) + , backendInitialized_(false) { } -/** - * @brief 析构函数 - * 确保模块正确关闭 - */ WindowModuleInitializer::~WindowModuleInitializer() { if (initialized_) { shutdown(); } - E2D_LOG_DEBUG("WindowModuleInitializer destructed"); } -/** - * @brief 初始化模块 - * 使用 BackendFactory 创建窗口实例 - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ -bool WindowModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) { - E2D_LOG_WARN("Window module already initialized"); - return true; - } - - if (!config) { - E2D_LOG_ERROR("Window module config is null"); +bool WindowModuleInitializer::initBackend() { +#ifdef E2D_BACKEND_SDL2 + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) { + E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError()); return false; } + E2D_LOG_INFO("SDL2 backend initialized"); + backendInitialized_ = true; + return true; +#endif + +#ifdef E2D_BACKEND_GLFW + if (!glfwInit()) { + E2D_LOG_ERROR("Failed to initialize GLFW"); + return false; + } + E2D_LOG_INFO("GLFW backend initialized"); + backendInitialized_ = true; + return true; +#endif + +#ifdef E2D_BACKEND_SWITCH + E2D_LOG_INFO("Switch backend (no init required)"); + backendInitialized_ = true; + return true; +#endif + + E2D_LOG_ERROR("No backend available"); + return false; +} + +void WindowModuleInitializer::shutdownBackend() { + if (!backendInitialized_) return; + +#ifdef E2D_BACKEND_SDL2 + SDL_Quit(); + E2D_LOG_INFO("SDL2 backend shutdown"); +#endif + +#ifdef E2D_BACKEND_GLFW + glfwTerminate(); + E2D_LOG_INFO("GLFW backend shutdown"); +#endif + + backendInitialized_ = false; +} + +bool WindowModuleInitializer::initialize(const IModuleConfig* config) { + if (initialized_) return true; const WindowModuleConfig* windowConfig = dynamic_cast(config); if (!windowConfig) { - E2D_LOG_ERROR("Invalid config type for window module"); + E2D_LOG_ERROR("Invalid window module config"); return false; } + + backend_ = windowConfig->backend; + windowConfig_ = windowConfig->windowConfig; - ModuleInfo info = config->getModuleInfo(); - moduleId_ = info.id; - - const std::string& backend = windowConfig->backend; - - if (!BackendFactory::has(backend)) { - E2D_LOG_ERROR("Backend '{}' not available", backend); +#ifdef __SWITCH__ + backend_ = "switch"; + windowConfig_.mode = WindowMode::Fullscreen; + windowConfig_.resizable = false; +#endif + + if (!initBackend()) { + return false; + } + +#ifdef E2D_BACKEND_SDL2 + extern void initSDL2Backend(); + initSDL2Backend(); +#endif + + if (!BackendFactory::has(backend_)) { + E2D_LOG_ERROR("Backend '{}' not available", backend_); auto backends = BackendFactory::backends(); if (backends.empty()) { E2D_LOG_ERROR("No backends registered!"); + shutdownBackend(); return false; } - std::string backendList; - for (const auto& b : backends) { - if (!backendList.empty()) backendList += ", "; - backendList += b; - } - E2D_LOG_WARN("Available backends: {}", backendList); + backend_ = backends[0]; + E2D_LOG_WARN("Using fallback backend: {}", backend_); + } + + if (!createWindow(backend_, windowConfig_)) { + E2D_LOG_ERROR("Failed to create window"); + shutdownBackend(); return false; } + + initialized_ = true; + E2D_LOG_INFO("Window module initialized"); + E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height()); + E2D_LOG_INFO(" Backend: {}", backend_); + E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync); + return true; +} + +bool WindowModuleInitializer::createWindow(const std::string& backend, const WindowConfigData& config) { window_ = BackendFactory::createWindow(backend); if (!window_) { E2D_LOG_ERROR("Failed to create window for backend: {}", backend); return false; } - - if (!window_->create(windowConfig->windowConfig)) { - E2D_LOG_ERROR("Failed to create window with given config"); - window_.reset(); + + if (!window_->create(config)) { + E2D_LOG_ERROR("Failed to create window"); return false; } - - initialized_ = true; - E2D_LOG_INFO("Window module initialized successfully (backend: {}, {}x{})", - backend, - windowConfig->windowConfig.width, - windowConfig->windowConfig.height); - + return true; } -/** - * @brief 关闭模块 - * 销毁窗口实例并重置状态 - */ void WindowModuleInitializer::shutdown() { - if (!initialized_) { - E2D_LOG_WARN("Window module not initialized, nothing to shutdown"); - return; - } + if (!initialized_) return; + + E2D_LOG_INFO("Window module shutting down"); if (window_) { window_->destroy(); window_.reset(); } - initialized_ = false; - moduleId_ = INVALID_MODULE_ID; + shutdownBackend(); - E2D_LOG_INFO("Window module shutdown complete"); + initialized_ = false; +} + +void register_window_module() { + if (s_windowModuleId != INVALID_MODULE_ID) return; + + s_windowModuleId = ModuleRegistry::instance().registerModule( + makeUnique(), + []() -> UniquePtr { + auto initializer = makeUnique(); + initializer->setModuleId(s_windowModuleId); + return initializer; + } + ); +} + +namespace { + struct WindowModuleAutoRegister { + WindowModuleAutoRegister() { + register_window_module(); + } + }; + + static WindowModuleAutoRegister s_autoRegister; } } // namespace extra2d diff --git a/Extra2D/src/utils/logger.cpp b/Extra2D/src/utils/logger.cpp index 7fdf894..f1ebdf9 100644 --- a/Extra2D/src/utils/logger.cpp +++ b/Extra2D/src/utils/logger.cpp @@ -1,114 +1,177 @@ #include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __SWITCH__ +#include +#endif namespace extra2d { -// 静态成员定义 LogLevel Logger::level_ = LogLevel::Info; bool Logger::initialized_ = false; bool Logger::consoleOutput_ = true; bool Logger::fileOutput_ = false; std::string Logger::logFile_; +void *Logger::logFileHandle_ = nullptr; -/** - * @brief 获取日志级别字符串 - * @param level 日志级别 - * @return 级别对应的字符串表示 - */ const char *Logger::getLevelString(LogLevel level) { - switch (level) { - case LogLevel::Trace: - return "TRACE"; - case LogLevel::Debug: - return "DEBUG"; - case LogLevel::Info: - return "INFO "; - case LogLevel::Warn: - return "WARN "; - case LogLevel::Error: - return "ERROR"; - case LogLevel::Fatal: - return "FATAL"; - default: - return "UNKNOWN"; - } + switch (level) { + case LogLevel::Trace: + return "TRACE"; + case LogLevel::Debug: + return "DEBUG"; + case LogLevel::Info: + return "INFO "; + case LogLevel::Warn: + return "WARN "; + case LogLevel::Error: + return "ERROR"; + case LogLevel::Fatal: + return "FATAL"; + default: + return "UNKNOWN"; + } +} + +void Logger::writeToConsole(LogLevel level, const char *msg) { + const char *levelStr = getLevelString(level); + +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + WORD color = 7; + switch (level) { + case LogLevel::Trace: color = 8; break; + case LogLevel::Debug: color = 8; break; + case LogLevel::Info: color = 7; break; + case LogLevel::Warn: color = 14; break; + case LogLevel::Error: color = 12; break; + case LogLevel::Fatal: color = 12 | FOREGROUND_INTENSITY; break; + default: break; + } + SetConsoleTextAttribute(hConsole, color); + printf("[%s] %s\n", levelStr, msg); + SetConsoleTextAttribute(hConsole, 7); +#else + const char *colorCode = "\033[0m"; + switch (level) { + case LogLevel::Trace: colorCode = "\033[90m"; break; + case LogLevel::Debug: colorCode = "\033[90m"; break; + case LogLevel::Info: colorCode = "\033[0m"; break; + case LogLevel::Warn: colorCode = "\033[33m"; break; + case LogLevel::Error: colorCode = "\033[31m"; break; + case LogLevel::Fatal: colorCode = "\033[1;31m"; break; + default: break; + } + printf("%s[%s] %s\033[0m\n", colorCode, levelStr, msg); +#endif +} + +void Logger::writeToFile(LogLevel level, const char *msg) { + if (!logFileHandle_) return; + + FILE *fp = static_cast(logFileHandle_); + + time_t now = time(nullptr); + struct tm *tm_info = localtime(&now); + char timeBuf[32]; + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info); + + fprintf(fp, "[%s] [%s] %s\n", timeBuf, getLevelString(level), msg); + fflush(fp); +} + +void Logger::outputLog(LogLevel level, const char *msg) { + if (consoleOutput_) { + writeToConsole(level, msg); + } + if (fileOutput_) { + writeToFile(level, msg); + } } -/** - * @brief 初始化日志系统 - * - * 初始化日志系统并设置SDL日志级别为详细模式,允许所有级别的日志输出 - */ void Logger::init() { - if (initialized_) { - return; - } + if (initialized_) { + return; + } - // 设置 SDL 日志级别为详细模式(允许所有级别的日志) - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE); +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD mode = 0; + if (GetConsoleMode(hOut, &mode)) { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hOut, mode); + } + } +#endif - initialized_ = true; - log(LogLevel::Info, "Logger initialized with SDL2"); +#ifdef __SWITCH__ + consoleInit(NULL); +#endif + + initialized_ = true; + log(LogLevel::Info, "Logger initialized"); } -/** - * @brief 关闭日志系统 - * - * 关闭日志系统并输出关闭日志 - */ void Logger::shutdown() { - if (initialized_) { - log(LogLevel::Info, "Logger shutting down"); - } - initialized_ = false; + if (initialized_) { + log(LogLevel::Info, "Logger shutting down"); + } + + if (logFileHandle_) { + fclose(static_cast(logFileHandle_)); + logFileHandle_ = nullptr; + } + +#ifdef __SWITCH__ + consoleExit(NULL); +#endif + + initialized_ = false; + fileOutput_ = false; } -/** - * @brief 设置日志级别 - * @param level 要设置的日志级别 - * - * 设置日志系统的最低输出级别,低于此级别的日志将被忽略 - */ void Logger::setLevel(LogLevel level) { - level_ = level; - // 同时设置 SDL 的日志级别 - if (level != LogLevel::Off) { - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, - static_cast(level)); - } + level_ = level; } -/** - * @brief 设置是否输出到控制台 - * @param enable true启用控制台输出,false禁用 - * - * 控制日志是否输出到控制台,通过调整SDL日志优先级实现 - */ void Logger::setConsoleOutput(bool enable) { - consoleOutput_ = enable; - // SDL2 日志默认输出到控制台,通过设置日志优先级控制 - if (!enable) { - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL); - } else { - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, - static_cast(level_)); - } + consoleOutput_ = enable; } -/** - * @brief 设置日志输出到文件 - * @param filename 日志文件路径 - * - * 配置日志输出到指定文件,空字符串则禁用文件输出 - */ void Logger::setFileOutput(const std::string &filename) { - logFile_ = filename; - fileOutput_ = !filename.empty(); - - if (fileOutput_) { - // SDL2 使用 SDL_LogSetOutputFunction 可以重定向日志输出 - // 这里我们记录文件路径,实际文件输出可以通过自定义回调实现 - log(LogLevel::Info, "File output configured: {}", filename); - } + if (logFileHandle_) { + fclose(static_cast(logFileHandle_)); + logFileHandle_ = nullptr; + } + + logFile_ = filename; + fileOutput_ = !filename.empty(); + + if (fileOutput_) { +#ifdef _WIN32 + FILE *fp = nullptr; + fopen_s(&fp, filename.c_str(), "a"); +#else + FILE *fp = fopen(filename.c_str(), "a"); +#endif + logFileHandle_ = fp; + + if (fp) { + time_t now = time(nullptr); + struct tm *tm_info = localtime(&now); + char timeBuf[32]; + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info); + fprintf(fp, "\n=== Log session started at %s ===\n", timeBuf); + fflush(fp); + } + } } } // namespace extra2d diff --git a/Extra2D/src/utils/logger_module.cpp b/Extra2D/src/utils/logger_module.cpp new file mode 100644 index 0000000..01713fd --- /dev/null +++ b/Extra2D/src/utils/logger_module.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace extra2d { + +bool LoggerModuleConfig::loadFromJson(const void* jsonData) { + if (!jsonData) return false; + + try { + const json& j = *static_cast(jsonData); + + if (j.contains("logLevel")) { + int level = j["logLevel"].get(); + if (level >= 0 && level <= 6) { + logLevel = static_cast(level); + } + } + + if (j.contains("consoleOutput")) { + consoleOutput = j["consoleOutput"].get(); + } + + if (j.contains("fileOutput")) { + fileOutput = j["fileOutput"].get(); + } + + if (j.contains("logFilePath")) { + logFilePath = j["logFilePath"].get(); + } + + return true; + } catch (...) { + return false; + } +} + +bool LoggerModuleConfig::saveToJson(void* jsonData) const { + if (!jsonData) return false; + + try { + json& j = *static_cast(jsonData); + j["logLevel"] = static_cast(logLevel); + j["consoleOutput"] = consoleOutput; + j["fileOutput"] = fileOutput; + j["logFilePath"] = logFilePath; + return true; + } catch (...) { + return false; + } +} + +LoggerModuleInitializer::LoggerModuleInitializer() + : moduleId_(INVALID_MODULE_ID) + , initialized_(false) { +} + +LoggerModuleInitializer::~LoggerModuleInitializer() { + if (initialized_) { + shutdown(); + } +} + +bool LoggerModuleInitializer::initialize(const IModuleConfig* config) { + if (initialized_) return true; + + const LoggerModuleConfig* loggerConfig = dynamic_cast(config); + + Logger::init(); + + if (loggerConfig) { + Logger::setLevel(loggerConfig->logLevel); + Logger::setConsoleOutput(loggerConfig->consoleOutput); + if (loggerConfig->fileOutput && !loggerConfig->logFilePath.empty()) { + Logger::setFileOutput(loggerConfig->logFilePath); + } + } + + initialized_ = true; + E2D_LOG_INFO("Logger module initialized"); + return true; +} + +void LoggerModuleInitializer::shutdown() { + if (!initialized_) return; + + E2D_LOG_INFO("Logger module shutting down"); + Logger::shutdown(); + initialized_ = false; +} + +namespace { + static ModuleId s_loggerModuleId = INVALID_MODULE_ID; + + struct LoggerModuleRegistrar { + LoggerModuleRegistrar() { + s_loggerModuleId = ModuleRegistry::instance().registerModule( + makeUnique(), + []() -> UniquePtr { + auto initializer = makeUnique(); + initializer->setModuleId(s_loggerModuleId); + return initializer; + } + ); + } + }; + + static LoggerModuleRegistrar s_registrar; +} + +} // namespace extra2d