diff --git a/Extra2D/src/graphics/core/render_module.cpp b/Extra2D/src/graphics/core/render_module.cpp index 220b2b9..4e5826e 100644 --- a/Extra2D/src/graphics/core/render_module.cpp +++ b/Extra2D/src/graphics/core/render_module.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include namespace extra2d { @@ -20,13 +20,12 @@ RenderModule::~RenderModule() { } static std::string getExecutableDir() { - char* basePath = SDL_GetBasePath(); - if (basePath) { - std::string path(basePath); - SDL_free(basePath); - return path; + try { + auto currentPath = std::filesystem::current_path(); + return currentPath.string() + "/"; + } catch (...) { + return "./"; } - return "./"; } bool RenderModule::init() { diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/opengl/gl_renderer.cpp index 2e4a6d9..a98da62 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/opengl/gl_renderer.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -157,8 +156,10 @@ void GLRenderer::setViewport(int x, int y, int width, int height) { */ void GLRenderer::setVSync(bool enabled) { vsync_ = enabled; - // 使用 SDL2 设置交换间隔 - SDL_GL_SetSwapInterval(enabled ? 1 : 0); + // 通过窗口接口设置垂直同步 + if (window_) { + window_->setVSync(enabled); + } } /** diff --git a/Extra2D/src/platform/backends/glfw/glfw_backend.cpp b/Extra2D/src/platform/backends/glfw/glfw_backend.cpp new file mode 100644 index 0000000..9468940 --- /dev/null +++ b/Extra2D/src/platform/backends/glfw/glfw_backend.cpp @@ -0,0 +1,28 @@ +#include "glfw_window.h" +#include "glfw_input.h" +#include + +namespace extra2d { + +namespace { + static bool s_glfwBackendRegistered = false; +} + +void initGLFWBackend() { + if (s_glfwBackendRegistered) { + return; + } + s_glfwBackendRegistered = true; + + BackendFactory::reg( + "glfw", + []() -> UniquePtr { + return makeUnique(); + }, + []() -> UniquePtr { + return makeUnique(); + } + ); +} + +} // namespace extra2d diff --git a/Extra2D/src/platform/backends/glfw/glfw_input.cpp b/Extra2D/src/platform/backends/glfw/glfw_input.cpp new file mode 100644 index 0000000..72605da --- /dev/null +++ b/Extra2D/src/platform/backends/glfw/glfw_input.cpp @@ -0,0 +1,426 @@ +#include "glfw_input.h" +#include +#include + +namespace extra2d { + +// GLFW 按键到引擎按键的映射 +static Key glfwToKey(int glfwKey) { + switch (glfwKey) { + // 字母键 + case GLFW_KEY_A: return Key::A; + case GLFW_KEY_B: return Key::B; + case GLFW_KEY_C: return Key::C; + case GLFW_KEY_D: return Key::D; + case GLFW_KEY_E: return Key::E; + case GLFW_KEY_F: return Key::F; + case GLFW_KEY_G: return Key::G; + case GLFW_KEY_H: return Key::H; + case GLFW_KEY_I: return Key::I; + case GLFW_KEY_J: return Key::J; + case GLFW_KEY_K: return Key::K; + case GLFW_KEY_L: return Key::L; + case GLFW_KEY_M: return Key::M; + case GLFW_KEY_N: return Key::N; + case GLFW_KEY_O: return Key::O; + case GLFW_KEY_P: return Key::P; + case GLFW_KEY_Q: return Key::Q; + case GLFW_KEY_R: return Key::R; + case GLFW_KEY_S: return Key::S; + case GLFW_KEY_T: return Key::T; + case GLFW_KEY_U: return Key::U; + case GLFW_KEY_V: return Key::V; + case GLFW_KEY_W: return Key::W; + case GLFW_KEY_X: return Key::X; + case GLFW_KEY_Y: return Key::Y; + case GLFW_KEY_Z: return Key::Z; + + // 数字键 + case GLFW_KEY_0: return Key::Num0; + case GLFW_KEY_1: return Key::Num1; + case GLFW_KEY_2: return Key::Num2; + case GLFW_KEY_3: return Key::Num3; + case GLFW_KEY_4: return Key::Num4; + case GLFW_KEY_5: return Key::Num5; + case GLFW_KEY_6: return Key::Num6; + case GLFW_KEY_7: return Key::Num7; + case GLFW_KEY_8: return Key::Num8; + case GLFW_KEY_9: return Key::Num9; + + // 功能键 + case GLFW_KEY_F1: return Key::F1; + case GLFW_KEY_F2: return Key::F2; + case GLFW_KEY_F3: return Key::F3; + case GLFW_KEY_F4: return Key::F4; + case GLFW_KEY_F5: return Key::F5; + case GLFW_KEY_F6: return Key::F6; + case GLFW_KEY_F7: return Key::F7; + case GLFW_KEY_F8: return Key::F8; + case GLFW_KEY_F9: return Key::F9; + case GLFW_KEY_F10: return Key::F10; + case GLFW_KEY_F11: return Key::F11; + case GLFW_KEY_F12: return Key::F12; + + // 特殊键 + case GLFW_KEY_SPACE: return Key::Space; + case GLFW_KEY_ENTER: return Key::Enter; + case GLFW_KEY_ESCAPE: return Key::Escape; + case GLFW_KEY_TAB: return Key::Tab; + case GLFW_KEY_BACKSPACE: return Key::Backspace; + case GLFW_KEY_INSERT: return Key::Insert; + case GLFW_KEY_DELETE: return Key::Delete; + case GLFW_KEY_HOME: return Key::Home; + case GLFW_KEY_END: return Key::End; + case GLFW_KEY_PAGE_UP: return Key::PageUp; + case GLFW_KEY_PAGE_DOWN: return Key::PageDown; + + // 方向键 + case GLFW_KEY_UP: return Key::Up; + case GLFW_KEY_DOWN: return Key::Down; + case GLFW_KEY_LEFT: return Key::Left; + case GLFW_KEY_RIGHT: return Key::Right; + + // 修饰键 + case GLFW_KEY_LEFT_SHIFT: return Key::LShift; + case GLFW_KEY_RIGHT_SHIFT: return Key::RShift; + case GLFW_KEY_LEFT_CONTROL: return Key::LCtrl; + case GLFW_KEY_RIGHT_CONTROL: return Key::RCtrl; + case GLFW_KEY_LEFT_ALT: return Key::LAlt; + case GLFW_KEY_RIGHT_ALT: return Key::RAlt; + + // 锁定键 + case GLFW_KEY_CAPS_LOCK: return Key::CapsLock; + case GLFW_KEY_NUM_LOCK: return Key::NumLock; + case GLFW_KEY_SCROLL_LOCK: return Key::ScrollLock; + + default: return Key::None; + } +} + +GLFWInput::GLFWInput() { + keyCurrent_.fill(false); + keyPrevious_.fill(false); + mouseCurrent_.fill(false); + mousePrevious_.fill(false); + gamepadCurrent_.fill(false); + gamepadPrevious_.fill(false); +} + +GLFWInput::~GLFWInput() { + shutdown(); +} + +void GLFWInput::init() { + E2D_LOG_INFO("GLFWInput initialized"); + openGamepad(); +} + +void GLFWInput::shutdown() { + closeGamepad(); + E2D_LOG_INFO("GLFWInput shutdown"); +} + +void GLFWInput::update() { + // 保存上一帧状态 + keyPrevious_ = keyCurrent_; + mousePrevious_ = mouseCurrent_; + gamepadPrevious_ = gamepadCurrent_; + + // 重置增量 + scrollDelta_ = 0.0f; + mouseDelta_ = Vec2{0.0f, 0.0f}; + + // 更新游戏手柄 + updateGamepad(); + + // 更新键盘状态(通过轮询 GLFW) + if (window_) { + for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; ++i) { + Key key = glfwToKey(i); + if (key != Key::None) { + int state = glfwGetKey(window_, i); + keyCurrent_[static_cast(key)] = (state == GLFW_PRESS); + } + } + + // 更新鼠标按钮状态 + for (int i = 0; i < static_cast(Mouse::Count); ++i) { + int glfwButton = GLFW_MOUSE_BUTTON_1 + i; + if (glfwButton <= GLFW_MOUSE_BUTTON_LAST) { + int state = glfwGetMouseButton(window_, glfwButton); + mouseCurrent_[i] = (state == GLFW_PRESS); + } + } + + // 获取鼠标位置 + double x, y; + glfwGetCursorPos(window_, &x, &y); + mousePos_ = Vec2{static_cast(x), static_cast(y)}; + } +} + +bool GLFWInput::down(Key key) const { + size_t idx = static_cast(key); + if (idx < keyCurrent_.size()) { + return keyCurrent_[idx]; + } + return false; +} + +bool GLFWInput::pressed(Key key) const { + size_t idx = static_cast(key); + if (idx < keyCurrent_.size()) { + return keyCurrent_[idx] && !keyPrevious_[idx]; + } + return false; +} + +bool GLFWInput::released(Key key) const { + size_t idx = static_cast(key); + if (idx < keyCurrent_.size()) { + return !keyCurrent_[idx] && keyPrevious_[idx]; + } + return false; +} + +bool GLFWInput::down(Mouse btn) const { + size_t idx = static_cast(btn); + if (idx < mouseCurrent_.size()) { + return mouseCurrent_[idx]; + } + return false; +} + +bool GLFWInput::pressed(Mouse btn) const { + size_t idx = static_cast(btn); + if (idx < mouseCurrent_.size()) { + return mouseCurrent_[idx] && !mousePrevious_[idx]; + } + return false; +} + +bool GLFWInput::released(Mouse btn) const { + size_t idx = static_cast(btn); + if (idx < mouseCurrent_.size()) { + return !mouseCurrent_[idx] && mousePrevious_[idx]; + } + return false; +} + +Vec2 GLFWInput::mouse() const { + return mousePos_; +} + +Vec2 GLFWInput::mouseDelta() const { + return mouseDelta_; +} + +float GLFWInput::scroll() const { + return scroll_; +} + +float GLFWInput::scrollDelta() const { + return scrollDelta_; +} + +void GLFWInput::setMouse(const Vec2& pos) { + if (window_) { + glfwSetCursorPos(window_, pos.x, pos.y); + } +} + +bool GLFWInput::gamepad() const { + return gamepadId_ != -1; +} + +bool GLFWInput::down(Gamepad btn) const { + size_t idx = static_cast(btn); + if (idx < gamepadCurrent_.size()) { + return gamepadCurrent_[idx]; + } + return false; +} + +bool GLFWInput::pressed(Gamepad btn) const { + size_t idx = static_cast(btn); + if (idx < gamepadCurrent_.size()) { + return gamepadCurrent_[idx] && !gamepadPrevious_[idx]; + } + return false; +} + +bool GLFWInput::released(Gamepad btn) const { + size_t idx = static_cast(btn); + if (idx < gamepadCurrent_.size()) { + return !gamepadCurrent_[idx] && gamepadPrevious_[idx]; + } + return false; +} + +Vec2 GLFWInput::leftStick() const { + return leftStick_; +} + +Vec2 GLFWInput::rightStick() const { + return rightStick_; +} + +float GLFWInput::leftTrigger() const { + return leftTrigger_; +} + +float GLFWInput::rightTrigger() const { + return rightTrigger_; +} + +void GLFWInput::vibrate(float left, float right) { + // GLFW 本身不支持震动,需要平台特定的代码 + // 这里可以扩展为使用平台特定的 API + (void)left; + (void)right; +} + +bool GLFWInput::touching() const { + return false; +} + +int GLFWInput::touchCount() const { + return 0; +} + +Vec2 GLFWInput::touch(int index) const { + (void)index; + return Vec2{0.0f, 0.0f}; +} + +TouchPoint GLFWInput::touchPoint(int index) const { + (void)index; + return TouchPoint{}; +} + +// 事件处理函数 +void GLFWInput::handleKeyEvent(int key, int scancode, int action, int mods) { + (void)scancode; + (void)mods; + + Key eKey = glfwToKey(key); + if (eKey != Key::None) { + size_t idx = static_cast(eKey); + if (action == GLFW_PRESS) { + keyCurrent_[idx] = true; + } else if (action == GLFW_RELEASE) { + keyCurrent_[idx] = false; + } + } +} + +void GLFWInput::handleMouseButtonEvent(int button, int action, int mods) { + (void)mods; + + if (button >= GLFW_MOUSE_BUTTON_1 && button <= GLFW_MOUSE_BUTTON_LAST) { + size_t idx = static_cast(button - GLFW_MOUSE_BUTTON_1); + if (idx < mouseCurrent_.size()) { + if (action == GLFW_PRESS) { + mouseCurrent_[idx] = true; + } else if (action == GLFW_RELEASE) { + mouseCurrent_[idx] = false; + } + } + } +} + +void GLFWInput::handleCursorPosEvent(double xpos, double ypos) { + Vec2 newPos{static_cast(xpos), static_cast(ypos)}; + mouseDelta_ = newPos - mousePos_; + mousePos_ = newPos; +} + +void GLFWInput::handleScrollEvent(double xoffset, double yoffset) { + (void)xoffset; + scroll_ += static_cast(yoffset); + scrollDelta_ += static_cast(yoffset); +} + +void GLFWInput::handleJoystickEvent(int jid, int event) { + if (event == GLFW_CONNECTED) { + E2D_LOG_INFO("Gamepad connected: {}", jid); + if (gamepadId_ == -1) { + openGamepad(); + } + } else if (event == GLFW_DISCONNECTED) { + if (jid == gamepadId_) { + E2D_LOG_INFO("Gamepad disconnected: {}", jid); + closeGamepad(); + } + } +} + +void GLFWInput::updateGamepad() { + if (gamepadId_ == -1) { + return; + } + + GLFWgamepadstate state; + if (!glfwGetGamepadState(gamepadId_, &state)) { + return; + } + + // 更新按钮状态 + gamepadCurrent_[static_cast(Gamepad::A)] = state.buttons[GLFW_GAMEPAD_BUTTON_A] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::B)] = state.buttons[GLFW_GAMEPAD_BUTTON_B] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::X)] = state.buttons[GLFW_GAMEPAD_BUTTON_X] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::Y)] = state.buttons[GLFW_GAMEPAD_BUTTON_Y] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::Back)] = state.buttons[GLFW_GAMEPAD_BUTTON_BACK] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::Start)] = state.buttons[GLFW_GAMEPAD_BUTTON_START] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::LStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::RStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::LB)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::RB)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::DUp)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::DDown)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::DLeft)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::DRight)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] == GLFW_PRESS; + gamepadCurrent_[static_cast(Gamepad::Guide)] = state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] == GLFW_PRESS; + + // 更新摇杆值(应用死区) + leftStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]); + leftStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]); + rightStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); + rightStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); + + // 更新扳机值(范围 [0, 1]) + leftTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) * 0.5f; + rightTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) * 0.5f; +} + +void GLFWInput::openGamepad() { + for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) { + if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) { + gamepadId_ = jid; + E2D_LOG_INFO("Gamepad opened: {}", glfwGetGamepadName(jid)); + return; + } + } +} + +void GLFWInput::closeGamepad() { + if (gamepadId_ != -1) { + gamepadId_ = -1; + gamepadCurrent_.fill(false); + gamepadPrevious_.fill(false); + leftStick_ = Vec2{0.0f, 0.0f}; + rightStick_ = Vec2{0.0f, 0.0f}; + leftTrigger_ = 0.0f; + rightTrigger_ = 0.0f; + } +} + +float GLFWInput::applyDeadzone(float value) const { + if (std::abs(value) < deadzone_) { + return 0.0f; + } + float sign = value >= 0.0f ? 1.0f : -1.0f; + return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_); +} + +} // namespace extra2d diff --git a/Extra2D/src/platform/backends/glfw/glfw_input.h b/Extra2D/src/platform/backends/glfw/glfw_input.h new file mode 100644 index 0000000..287d90e --- /dev/null +++ b/Extra2D/src/platform/backends/glfw/glfw_input.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief GLFW 输入实现 + */ +class GLFWInput : public IInput { +public: + GLFWInput(); + ~GLFWInput() override; + + void init() override; + void shutdown() override; + void update() override; + + // Keyboard + bool down(Key key) const override; + bool pressed(Key key) const override; + bool released(Key key) const override; + + // Mouse + bool down(Mouse btn) const override; + bool pressed(Mouse btn) const override; + bool released(Mouse btn) const override; + Vec2 mouse() const override; + Vec2 mouseDelta() const override; + float scroll() const override; + float scrollDelta() const override; + void setMouse(const Vec2& pos) override; + + // Gamepad + bool gamepad() const override; + bool down(Gamepad btn) const override; + bool pressed(Gamepad btn) const override; + bool released(Gamepad btn) const override; + Vec2 leftStick() const override; + Vec2 rightStick() const override; + float leftTrigger() const override; + float rightTrigger() const override; + void vibrate(float left, float right) override; + + // Touch + bool touching() const override; + int touchCount() const override; + Vec2 touch(int index) const override; + TouchPoint touchPoint(int index) const override; + + // GLFW specific + void handleKeyEvent(int key, int scancode, int action, int mods); + void handleMouseButtonEvent(int button, int action, int mods); + void handleCursorPosEvent(double xpos, double ypos); + void handleScrollEvent(double xoffset, double yoffset); + void handleJoystickEvent(int jid, int event); + + void setWindow(GLFWwindow* window) { window_ = window; } + +private: + void updateGamepad(); + void openGamepad(); + void closeGamepad(); + + float applyDeadzone(float value) const; + + // Keyboard state + std::array(Key::Count)> keyCurrent_; + std::array(Key::Count)> keyPrevious_; + + // Mouse state + std::array(Mouse::Count)> mouseCurrent_; + std::array(Mouse::Count)> mousePrevious_; + Vec2 mousePos_; + Vec2 mouseDelta_; + float scroll_ = 0.0f; + float scrollDelta_ = 0.0f; + + // Gamepad state + std::array(Gamepad::Count)> gamepadCurrent_; + std::array(Gamepad::Count)> gamepadPrevious_; + Vec2 leftStick_; + Vec2 rightStick_; + float leftTrigger_ = 0.0f; + float rightTrigger_ = 0.0f; + int gamepadId_ = -1; + + float deadzone_ = 0.15f; + + GLFWwindow* window_ = nullptr; +}; + +} // namespace extra2d diff --git a/Extra2D/src/platform/backends/glfw/glfw_window.cpp b/Extra2D/src/platform/backends/glfw/glfw_window.cpp new file mode 100644 index 0000000..44f01b3 --- /dev/null +++ b/Extra2D/src/platform/backends/glfw/glfw_window.cpp @@ -0,0 +1,462 @@ +#include "glfw_window.h" +#include "glfw_input.h" +#include +#include + +namespace extra2d { + +GLFWWindow::GLFWWindow() {} + +GLFWWindow::~GLFWWindow() { + destroy(); +} + +bool GLFWWindow::create(const WindowConfigData& cfg) { + if (!initGLFW()) { + return false; + } + + // 设置 OpenGL ES 3.2 上下文 + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); + glfwWindowHint(GLFW_DEPTH_BITS, 24); + glfwWindowHint(GLFW_STENCIL_BITS, 8); + +#ifdef __SWITCH__ + // Switch 平台强制全屏 + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + fullscreen_ = true; +#else + // 桌面平台配置 + if (cfg.resizable) { + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + } else { + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + } + + if (!cfg.decorated) { + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + } + + if (cfg.isFullscreen()) { + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + fullscreen_ = true; + } +#endif + + if (cfg.multisamples > 0) { + glfwWindowHint(GLFW_SAMPLES, cfg.multisamples); + } + + // 创建窗口 + GLFWmonitor* monitor = nullptr; +#ifdef __SWITCH__ + monitor = glfwGetPrimaryMonitor(); +#endif + if (fullscreen_ && !monitor) { + monitor = glfwGetPrimaryMonitor(); + } + + glfwWindow_ = glfwCreateWindow( + cfg.width, cfg.height, + cfg.title.c_str(), + monitor, + nullptr + ); + + if (!glfwWindow_) { + E2D_LOG_ERROR("Failed to create GLFW window"); + deinitGLFW(); + return false; + } + + glfwMakeContextCurrent(glfwWindow_); + + // 初始化 GLAD + if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) { + E2D_LOG_ERROR("Failed to initialize GLAD GLES2"); + glfwDestroyWindow(glfwWindow_); + glfwWindow_ = nullptr; + deinitGLFW(); + return false; + } + + // 设置垂直同步 + glfwSwapInterval(cfg.vsync ? 1 : 0); + vsync_ = cfg.vsync; + + // 获取实际窗口大小 + glfwGetWindowSize(glfwWindow_, &width_, &height_); + updateContentScale(); + + // 设置回调函数 + glfwSetWindowUserPointer(glfwWindow_, this); + glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback); + glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback); + glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback); + glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback); + glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback); + glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback); + glfwSetScrollCallback(glfwWindow_, scrollCallback); + glfwSetKeyCallback(glfwWindow_, keyCallback); + glfwSetJoystickCallback(joystickCallback); + + // 创建输入系统 + input_ = makeUnique(); + input_->setWindow(glfwWindow_); + input_->init(); + + E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_); + E2D_LOG_INFO(" Platform: OpenGL ES 3.2"); + return true; +} + +void GLFWWindow::destroy() { + if (input_) { + input_->shutdown(); + input_.reset(); + } + + if (glfwWindow_) { + glfwDestroyWindow(glfwWindow_); + glfwWindow_ = nullptr; + } + + deinitGLFW(); +} + +void GLFWWindow::poll() { + if (!glfwWindow_) return; + + if (input_) { + input_->update(); + } + + glfwPollEvents(); +} + +void GLFWWindow::swap() { + if (glfwWindow_) { + glfwSwapBuffers(glfwWindow_); + } +} + +bool GLFWWindow::shouldClose() const { + if (!glfwWindow_) return true; + return shouldClose_ || glfwWindowShouldClose(glfwWindow_); +} + +void GLFWWindow::close() { + shouldClose_ = true; + if (glfwWindow_) { + glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE); + } +} + +void GLFWWindow::setTitle(const std::string& title) { + if (glfwWindow_) { + glfwSetWindowTitle(glfwWindow_, title.c_str()); + } +} + +void GLFWWindow::setSize(int w, int h) { + if (glfwWindow_) { + glfwSetWindowSize(glfwWindow_, w, h); + width_ = w; + height_ = h; + } +} + +void GLFWWindow::setPos(int x, int y) { +#ifndef __SWITCH__ + if (glfwWindow_) { + glfwSetWindowPos(glfwWindow_, x, y); + } +#else + (void)x; + (void)y; +#endif +} + +void GLFWWindow::setFullscreen(bool fs) { +#ifndef __SWITCH__ + if (!glfwWindow_) return; + + if (fs == fullscreen_) return; + + if (fs) { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + } else { + glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0); + } + fullscreen_ = fs; + glfwGetWindowSize(glfwWindow_, &width_, &height_); + updateContentScale(); +#else + (void)fs; +#endif +} + +void GLFWWindow::setVSync(bool vsync) { + if (glfwWindow_) { + glfwSwapInterval(vsync ? 1 : 0); + vsync_ = vsync; + } +} + +void GLFWWindow::setVisible(bool visible) { +#ifndef __SWITCH__ + if (glfwWindow_) { + if (visible) { + glfwShowWindow(glfwWindow_); + } else { + glfwHideWindow(glfwWindow_); + } + } +#else + (void)visible; +#endif +} + +int GLFWWindow::width() const { + return width_; +} + +int GLFWWindow::height() const { + return height_; +} + +Size GLFWWindow::size() const { + return Size(static_cast(width_), static_cast(height_)); +} + +Vec2 GLFWWindow::pos() const { + int x = 0, y = 0; +#ifndef __SWITCH__ + if (glfwWindow_) { + glfwGetWindowPos(glfwWindow_, &x, &y); + } +#endif + return Vec2(static_cast(x), static_cast(y)); +} + +bool GLFWWindow::fullscreen() const { + return fullscreen_; +} + +bool GLFWWindow::vsync() const { + return vsync_; +} + +bool GLFWWindow::focused() const { + return focused_; +} + +bool GLFWWindow::minimized() const { + return minimized_; +} + +float GLFWWindow::scaleX() const { + return scaleX_; +} + +float GLFWWindow::scaleY() const { + return scaleY_; +} + +void GLFWWindow::setCursor(Cursor cursor) { +#ifndef __SWITCH__ + if (!glfwWindow_) return; + + if (cursor == Cursor::Hidden) { + glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + return; + } + + glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + GLFWcursor* glfwCursor = nullptr; + switch (cursor) { + case Cursor::Arrow: + glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + break; + case Cursor::IBeam: + glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + break; + case Cursor::Crosshair: + glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); + break; + case Cursor::Hand: + glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + break; + case Cursor::HResize: + glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + break; + case Cursor::VResize: + glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + break; + default: + glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + break; + } + + if (glfwCursor) { + glfwSetCursor(glfwWindow_, glfwCursor); + } +#else + (void)cursor; +#endif +} + +void GLFWWindow::showCursor(bool show) { +#ifndef __SWITCH__ + if (glfwWindow_) { + glfwSetInputMode(glfwWindow_, GLFW_CURSOR, show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN); + cursorVisible_ = show; + } +#else + (void)show; +#endif +} + +void GLFWWindow::lockCursor(bool lock) { +#ifndef __SWITCH__ + if (glfwWindow_) { + glfwSetInputMode(glfwWindow_, GLFW_CURSOR, lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); + cursorLocked_ = lock; + } +#else + (void)lock; +#endif +} + +IInput* GLFWWindow::input() const { + return input_.get(); +} + +void GLFWWindow::onResize(ResizeCb cb) { + resizeCb_ = cb; +} + +void GLFWWindow::onClose(CloseCb cb) { + closeCb_ = cb; +} + +void GLFWWindow::onFocus(FocusCb cb) { + focusCb_ = cb; +} + +void* GLFWWindow::native() const { + return glfwWindow_; +} + +bool GLFWWindow::initGLFW() { + static int glfwInitCount = 0; + if (glfwInitCount == 0) { + if (!glfwInit()) { + E2D_LOG_ERROR("Failed to initialize GLFW"); + return false; + } + glfwInitCount++; + } + return true; +} + +void GLFWWindow::deinitGLFW() { + static int glfwInitCount = 1; + glfwInitCount--; + if (glfwInitCount == 0) { + glfwTerminate(); + } +} + +void GLFWWindow::updateContentScale() { + if (glfwWindow_) { + int fbWidth, fbHeight; + glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight); + scaleX_ = fbWidth > 0 ? static_cast(fbWidth) / width_ : 1.0f; + scaleY_ = fbHeight > 0 ? static_cast(fbHeight) / height_ : 1.0f; + } +} + +// 静态回调函数 +void GLFWWindow::framebufferSizeCallback(GLFWwindow* window, int width, int height) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self) { + self->width_ = width; + self->height_ = height; + self->updateContentScale(); + if (self->resizeCb_) { + self->resizeCb_(width, height); + } + } +} + +void GLFWWindow::windowCloseCallback(GLFWwindow* window) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self) { + self->shouldClose_ = true; + if (self->closeCb_) { + self->closeCb_(); + } + } +} + +void GLFWWindow::windowFocusCallback(GLFWwindow* window, int focused) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self) { + self->focused_ = (focused == GLFW_TRUE); + if (self->focusCb_) { + self->focusCb_(self->focused_); + } + } +} + +void GLFWWindow::windowIconifyCallback(GLFWwindow* window, int iconified) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self) { + self->minimized_ = (iconified == GLFW_TRUE); + } +} + +void GLFWWindow::cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self && self->input_) { + self->input_->handleCursorPosEvent(xpos, ypos); + } +} + +void GLFWWindow::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self && self->input_) { + self->input_->handleMouseButtonEvent(button, action, mods); + } +} + +void GLFWWindow::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self && self->input_) { + self->input_->handleScrollEvent(xoffset, yoffset); + } +} + +void GLFWWindow::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + GLFWWindow* self = static_cast(glfwGetWindowUserPointer(window)); + if (self && self->input_) { + self->input_->handleKeyEvent(key, scancode, action, mods); + } +} + +void GLFWWindow::joystickCallback(int jid, int event) { + // 通过全局回调找到对应的窗口实例 + // 由于 GLFW 的 joystick 回调没有窗口参数,我们需要其他方式处理 + // 这里简化处理,让输入系统在 update() 中轮询 +} + +} // namespace extra2d diff --git a/Extra2D/src/platform/backends/glfw/glfw_window.h b/Extra2D/src/platform/backends/glfw/glfw_window.h new file mode 100644 index 0000000..7166a45 --- /dev/null +++ b/Extra2D/src/platform/backends/glfw/glfw_window.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +namespace extra2d { + +class GLFWInput; + +/** + * @brief GLFW 窗口实现 + */ +class GLFWWindow : public IWindow { +public: + GLFWWindow(); + ~GLFWWindow() override; + + bool create(const WindowConfigData& cfg) override; + void destroy() override; + + void poll() override; + void swap() override; + bool shouldClose() const override; + void close() override; + + void setTitle(const std::string& title) override; + void setSize(int w, int h) override; + void setPos(int x, int y) override; + void setFullscreen(bool fs) override; + void setVSync(bool vsync) override; + void setVisible(bool visible) override; + + int width() const override; + int height() const override; + Size size() const override; + Vec2 pos() const override; + bool fullscreen() const override; + bool vsync() const override; + bool focused() const override; + bool minimized() const override; + + float scaleX() const override; + float scaleY() const override; + + void setCursor(Cursor cursor) override; + void showCursor(bool show) override; + void lockCursor(bool lock) override; + + IInput* input() const override; + + void onResize(ResizeCb cb) override; + void onClose(CloseCb cb) override; + void onFocus(FocusCb cb) override; + + void* native() const override; + + /** + * @brief 获取 GLFW 窗口句柄 + */ + GLFWwindow* glfwWindow() const { return glfwWindow_; } + +private: + bool initGLFW(); + void deinitGLFW(); + void updateContentScale(); + + // GLFW 回调函数(静态) + static void framebufferSizeCallback(GLFWwindow* window, int width, int height); + static void windowCloseCallback(GLFWwindow* window); + static void windowFocusCallback(GLFWwindow* window, int focused); + static void windowIconifyCallback(GLFWwindow* window, int iconified); + static void cursorPosCallback(GLFWwindow* window, double xpos, double ypos); + static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods); + static void scrollCallback(GLFWwindow* window, double xoffset, double yoffset); + static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + static void joystickCallback(int jid, int event); + + GLFWwindow* glfwWindow_ = nullptr; + + UniquePtr input_; + + int width_ = 1280; + int height_ = 720; + bool fullscreen_ = false; + bool vsync_ = true; + bool focused_ = true; + bool minimized_ = false; + bool shouldClose_ = false; + float scaleX_ = 1.0f; + float scaleY_ = 1.0f; + bool cursorVisible_ = true; + bool cursorLocked_ = false; + + ResizeCb resizeCb_; + CloseCb closeCb_; + FocusCb focusCb_; +}; + +} // namespace extra2d diff --git a/Extra2D/src/platform/window_module.cpp b/Extra2D/src/platform/window_module.cpp index 02767dc..7c8a23f 100644 --- a/Extra2D/src/platform/window_module.cpp +++ b/Extra2D/src/platform/window_module.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #ifdef __SWITCH__ #include @@ -10,8 +9,12 @@ namespace extra2d { -// 前向声明 SDL2 后端初始化函数 +// 前向声明后端初始化函数 +#if defined(E2D_BACKEND_SDL2) void initSDL2Backend(); +#elif defined(E2D_BACKEND_GLFW) +void initGLFWBackend(); +#endif WindowModule::WindowModule(const Cfg& cfg) : cfg_(cfg) {} @@ -28,21 +31,16 @@ bool WindowModule::init() { cfg_.mode = WindowMode::Fullscreen; #endif - // 初始化SDL后端(注册到工厂) + // 初始化后端(注册到工厂) +#if defined(E2D_BACKEND_SDL2) initSDL2Backend(); - - // 初始化SDL - Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER; -#ifdef __SWITCH__ - flags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER; +#elif defined(E2D_BACKEND_GLFW) + initGLFWBackend(); +#else + #error "No window backend defined" #endif - if (SDL_Init(flags) != 0) { - E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError()); - return false; - } - sdlInited_ = true; - E2D_LOG_INFO("SDL initialized successfully"); + E2D_LOG_INFO("Window backend initialized"); // 创建窗口配置 WindowConfigData winCfg; @@ -57,8 +55,7 @@ bool WindowModule::init() { // 创建窗口(使用配置的后端) win_ = BackendFactory::createWindow(cfg_.backend); if (!win_) { - E2D_LOG_ERROR("Failed to create window backend"); - shutdown(); + E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend); return false; } @@ -81,11 +78,6 @@ void WindowModule::shutdown() { win_.reset(); } - if (sdlInited_) { - SDL_Quit(); - sdlInited_ = false; - } - initialized_ = false; } diff --git a/README.md b/README.md index 735bc41..33fdfce 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ SDL2 + + GLFW + Nintendo Switch @@ -174,6 +177,8 @@ sudo apt install xmake ### 构建项目 +#### 基础构建(使用 SDL2 后端,默认) + ```bash # 配置项目 xmake f -p mingw -a x86_64 -m release -y @@ -185,6 +190,32 @@ xmake build xmake run demo_basic ``` +#### 使用 GLFW 后端 + +```bash +# 配置项目(指定 GLFW 后端) +xmake f -p mingw -a x86_64 -m release --window_backend=glfw -y + +# 构建 +xmake build + +# 运行示例 +xmake run demo_basic +``` + +#### 切换后端 + +```bash +# 切换到 GLFW 后端 +xmake f --window_backend=glfw -y + +# 切换回 SDL2 后端 +xmake f --window_backend=sdl2 -y + +# 清理重新配置 +xmake f -c -y +``` + ### Nintendo Switch 构建 ```bash @@ -314,7 +345,8 @@ xmake run demo_basic | 技术 | 用途 | 版本 | |:----:|:-----|:----:| | OpenGL ES | 2D 图形渲染 | 3.2 | -| SDL2 | 窗口和输入管理 | 2.0+ | +| SDL2 | 窗口和输入管理(可选) | 2.0+ | +| GLFW | 窗口和输入管理(可选) | 3.4+ | | GLM | 数学库 | 0.9.9+ | | nlohmann_json | JSON 解析 | 3.x | | glad | OpenGL 加载器 | 最新版 | @@ -375,54 +407,8 @@ Extra2D/ │ │ └── opengl/ # OpenGL 实现 │ ├── platform/ # 平台实现 │ │ └── backends/ # 后端实现 -│ │ └── sdl2/ # SDL2 后端 -│ ├── scene/ # 场景实现 -│ ├── services/ # 服务实现 -│ └── utils/ # 工具实现 -├── docs/ # 文档 -├── examples/ # 示例程序 -│ └── basic/ # 基础示例 -└── xmake/ # 构建配置 - └── toolchains/ # 工具链配置 -``` -Extra2D/ -├── Extra2D/ -│ ├── include/ -│ │ ├── KHR/ # KHR 平台头文件 -│ │ ├── extra2d/ # 引擎公共头文件 -│ │ │ ├── app/ # 应用程序 -│ │ │ ├── audio/ # 音频配置 -│ │ │ ├── config/ # 配置系统 -│ │ │ ├── core/ # 核心类型 -│ │ │ ├── debug/ # 调试配置 -│ │ │ ├── event/ # 事件系统 -│ │ │ ├── graphics/ # 图形渲染 -│ │ │ │ └── opengl/ # OpenGL 实现 -│ │ │ ├── input/ # 输入配置 -│ │ │ ├── platform/ # 平台抽象 -│ │ │ ├── resource/ # 资源配置 -│ │ │ ├── scene/ # 场景系统 -│ │ │ ├── services/ # 服务接口 -│ │ │ └── utils/ # 工具库 -│ │ ├── glad/ # OpenGL 加载器 -│ │ └── stb/ # STB 单文件库 -│ ├── shaders/ # 着色器文件 -│ │ ├── builtin/ # 内置着色器 -│ │ ├── common/ # 公共着色器代码 -│ │ └── effects/ # 特效着色器 -│ └── src/ # 源文件 -│ ├── app/ # 应用实现 -│ ├── config/ # 配置实现 -│ ├── core/ # 核心实现 -│ ├── debug/ # 调试实现 -│ ├── event/ # 事件实现 -│ ├── glad/ # GLAD 实现 -│ ├── graphics/ # 图形实现 -│ │ └── opengl/ # OpenGL 实现 -│ ├── platform/ # 平台实现 -│ │ └── backends/ # 后端实现 -│ │ └── sdl2/ # SDL2 后端 -│ ├── resource/ # 资源实现 +│ │ ├── sdl2/ # SDL2 后端 +│ │ └── glfw/ # GLFW 后端 │ ├── scene/ # 场景实现 │ ├── services/ # 服务实现 │ └── utils/ # 工具实现 @@ -439,10 +425,10 @@ Extra2D/ | 平台 | 窗口后端 | 图形 API | 状态 | |-----|---------|---------|------| -| Windows | SDL2 | OpenGL ES 3.2 | ✅ 支持 | -| Linux | SDL2 | OpenGL ES 3.2 | ✅ 支持 | -| macOS | SDL2 | OpenGL ES 3.2 | ✅ 支持 | -| Nintendo Switch | SDL2 | OpenGL ES 3.2 | ✅ 支持 | +| Windows | SDL2 / GLFW | OpenGL ES 3.2 | ✅ 支持 | +| Linux | SDL2 / GLFW | OpenGL ES 3.2 | ✅ 支持 | +| macOS | SDL2 / GLFW | OpenGL ES 3.2 | ✅ 支持 | +| Nintendo Switch | SDL2 / GLFW | OpenGL ES 3.2 | ✅ 支持 | --- diff --git a/docs/module_system.md b/docs/module_system.md index 5a0fda1..f79b3a4 100644 --- a/docs/module_system.md +++ b/docs/module_system.md @@ -458,7 +458,7 @@ if (caps.supportsGamepad) { **职责**:窗口创建和管理 -**后端**:统一使用 SDL2,支持所有平台 +**后端**:支持 SDL2 和 GLFW,可通过配置切换 **配置**: ```cpp @@ -468,7 +468,16 @@ cfg.w = 1280; cfg.h = 720; cfg.mode = WindowMode::Windowed; cfg.vsync = true; -cfg.backend = "sdl2"; // 可选:指定后端 +cfg.backend = "sdl2"; // 可选:"sdl2" 或 "glfw" +``` + +**构建时选择后端**: +```bash +# 使用 SDL2 后端(默认) +xmake f --window_backend=sdl2 -y + +# 使用 GLFW 后端 +xmake f --window_backend=glfw -y ``` **平台约束**: @@ -678,10 +687,10 @@ eventService->addListener(EventType::MouseButtonPressed, [](Event& e) { | 平台 | 窗口后端 | 图形 API | 特殊处理 | |-----|---------|---------|---------| -| Windows | SDL2 | OpenGL ES 3.2 | - | -| Linux | SDL2 | OpenGL ES 3.2 | - | -| macOS | SDL2 | OpenGL ES 3.2 | - | -| Nintendo Switch | SDL2 | OpenGL ES 3.2 | romfs, 强制全屏 | +| Windows | SDL2 / GLFW | OpenGL ES 3.2 | - | +| Linux | SDL2 / GLFW | OpenGL ES 3.2 | - | +| macOS | SDL2 / GLFW | OpenGL ES 3.2 | - | +| Nintendo Switch | SDL2 / GLFW | OpenGL ES 3.2 | romfs, 强制全屏 | ### 平台检测 diff --git a/xmake.lua b/xmake.lua index f629793..1775063 100644 --- a/xmake.lua +++ b/xmake.lua @@ -8,7 +8,7 @@ -- - macOS -- - Nintendo Switch -- --- 窗口后端: SDL2 (统一) +-- 窗口后端: SDL2 / GLFW -- ============================================== -- 项目元信息 @@ -33,6 +33,13 @@ option("debug_logs") set_description("Enable debug logging") option_end() +option("window_backend") + set_default("sdl2") + set_showmenu(true) + set_description("Window backend: sdl2 or glfw") + set_values("sdl2", "glfw") +option_end() + -- ============================================== -- 平台检测与配置 -- ============================================== @@ -85,18 +92,25 @@ end if target_plat ~= "switch" then add_requires("glm") add_requires("nlohmann_json") - local sdl2_configs = { - configs = { - wayland = false + + -- 窗口后端依赖 + local backend = get_config("window_backend") or "sdl2" + if backend == "glfw" then + add_requires("glfw") + else + local sdl2_configs = { + configs = { + wayland = false + } } - } - if target_plat == "linux" then - local is_wayland = os.getenv("XDG_SESSION_TYPE") == "wayland" - if is_wayland then - sdl2_configs.configs.wayland = true + if target_plat == "linux" then + local is_wayland = os.getenv("XDG_SESSION_TYPE") == "wayland" + if is_wayland then + sdl2_configs.configs.wayland = true + end end + add_requires("libsdl2", sdl2_configs) end - add_requires("libsdl2", sdl2_configs) end @@ -150,14 +164,30 @@ target("demo_basic") -- 平台配置 local plat = get_config("plat") or os.host() + local backend = get_config("window_backend") or "sdl2" if plat == "mingw" or plat == "windows" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") elseif plat == "linux" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_syslinks("GL", "dl", "pthread") elseif plat == "macosx" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") end @@ -176,14 +206,30 @@ target("demo_hello_module") -- 平台配置 local plat = get_config("plat") or os.host() + local backend = get_config("window_backend") or "sdl2" if plat == "mingw" or plat == "windows" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") elseif plat == "linux" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_syslinks("GL", "dl", "pthread") elseif plat == "macosx" then - add_packages("glm", "nlohmann_json", "libsdl2") + add_packages("glm", "nlohmann_json") + if backend == "glfw" then + add_packages("glfw") + else + add_packages("libsdl2") + end add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") end diff --git a/xmake/engine.lua b/xmake/engine.lua index 8dcc08c..1cdf5c8 100644 --- a/xmake/engine.lua +++ b/xmake/engine.lua @@ -2,11 +2,11 @@ -- Extra2D 引擎库共享配置 -- 被主项目和示例共享使用 -- --- 窗口后端统一使用 SDL2,支持以下平台: --- - Windows (MinGW) --- - Linux --- - macOS --- - Nintendo Switch +-- 窗口后端支持 SDL2 和 GLFW: +-- - Windows (MinGW) - 支持 SDL2 和 GLFW +-- - Linux - 支持 SDL2 和 GLFW +-- - macOS - 支持 SDL2 和 GLFW +-- - Nintendo Switch - 使用系统提供的后端 -- ============================================== -- 获取当前平台 @@ -14,18 +14,29 @@ local function get_current_plat() return get_config("plat") or os.host() end +-- 获取窗口后端 +local function get_window_backend() + return get_config("window_backend") or "sdl2" +end + -- 定义 Extra2D 引擎库目标 function define_extra2d_engine() target("extra2d") set_kind("static") -- 引擎核心源文件 - add_files("Extra2D/src/**.cpp") + add_files("Extra2D/src/**.cpp|platform/backends/**.cpp") add_files("Extra2D/src/glad/glad.c") - -- SDL2 后端源文件(所有平台统一使用) - add_files("Extra2D/src/platform/backends/sdl2/*.cpp") - add_defines("E2D_BACKEND_SDL2") + -- 窗口后端源文件 + local backend = get_window_backend() + if backend == "glfw" then + add_files("Extra2D/src/platform/backends/glfw/*.cpp") + add_defines("E2D_BACKEND_GLFW") + else + add_files("Extra2D/src/platform/backends/sdl2/*.cpp") + add_defines("E2D_BACKEND_SDL2") + end -- 头文件路径 add_includedirs("Extra2D/include", {public = true}) @@ -39,23 +50,48 @@ function define_extra2d_engine() local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true}) add_linkdirs(devkitPro .. "/portlibs/switch/lib") - add_syslinks("SDL2", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m", - {public = true}) + + -- Switch 平台根据后端选择链接库 + local backend = get_window_backend() + if backend == "glfw" then + -- GLFW 后端(使用 libnx 提供的 GLFW) + add_syslinks("glfw", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m", + {public = true}) + else + -- SDL2 后端 + add_syslinks("SDL2", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m", + {public = true}) + end elseif plat == "mingw" or plat == "windows" then -- Windows (MinGW) 平台配置 add_packages("glm", "nlohmann_json", {public = true}) - add_packages("libsdl2", {public = true}) + local backend = get_window_backend() + if backend == "glfw" then + add_packages("glfw", {public = true}) + else + add_packages("libsdl2", {public = true}) + end add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi", {public = true}) elseif plat == "linux" then -- Linux 平台配置 add_packages("glm", "nlohmann_json", {public = true}) - add_packages("libsdl2", {public = true}) + local backend = get_window_backend() + if backend == "glfw" then + add_packages("glfw", {public = true}) + else + add_packages("libsdl2", {public = true}) + end add_syslinks("GL", "dl", "pthread", {public = true}) elseif plat == "macosx" then -- macOS 平台配置 add_packages("glm", "nlohmann_json", {public = true}) - add_packages("libsdl2", {public = true}) + local backend = get_window_backend() + if backend == "glfw" then + add_packages("glfw", {public = true}) + else + add_packages("libsdl2", {public = true}) + end add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo", {public = true}) end