You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在GLWindow封装类中向glfwSetKeyCallback传递自定义参数回调?

解决GLFW回调与自定义枚举参数的适配问题

我来帮你搞定这个回调适配的问题。核心矛盾在于GLFW只接受C风格的函数指针作为回调,而且参数是原始的int类型,而你想要用自定义的枚举类型和std::function来封装。下面是完整的解决方案:

核心思路

  1. 调整GLWindow中回调类型的定义,让它匹配你期望的、使用自定义枚举的签名
  2. 在GLWindow类中添加成员变量,存储用户设置的回调函数
  3. 编写静态的C风格适配函数,作为GLFW的回调入口——这个函数会把GLFW传来的int参数转换成你的自定义枚举,再调用用户的回调
  4. 在GLWindow构造时就把这些静态适配函数注册给GLFW,后续通过更新成员变量来切换回调

修改后的完整代码

Window.h

#include <glad/glad.h>
#include <glfw/glfw3.h>
#include <functional>
#include <string>
#include <iostream>

enum class Modifier { NoModifier = 0, Shift = 1, Control = 2, Alt = 4, Super = 8 };
enum class Action { Release = 0, Press = 1, Repeat = 2 };
enum class ButtonCode { Button_0 = 0 /* 这里补全GLFW的所有按键码 */ };
enum class KeyCode { UNKNOWN = -1, Space = 32 /* 这里补全GLFW的所有键码 */ };

class GLWindow {
public:
    // 修正回调类型定义,匹配你想要的自定义枚举签名
    using KeyCallback = std::function<void(KeyCode, Action, Modifier)>;
    using CursorPosCallback = std::function<void(double, double)>;
    using MouseCallback = std::function<void(ButtonCode, Action, Modifier, double, double)>;
    using ScrollCallback = std::function<void(double, double)>;

    GLWindow(const std::string& title, uint32_t width, uint32_t height);
    ~GLWindow();

    uint32_t getWidth() const;
    uint32_t getHeight() const;

    void setKeyCallback(const KeyCallback& callback);
    void setCursorPosCallback(const CursorPosCallback& callback);
    void setMouseCallback(const MouseCallback& callback);
    void setScrollCallback(const ScrollCallback& callback);

    GLFWwindow* getGLFWHandle() const;

private:
    GLFWwindow* handle;
    // 存储用户设置的回调函数
    KeyCallback m_keyCallback;
    CursorPosCallback m_cursorPosCallback;
    MouseCallback m_mouseCallback;
    ScrollCallback m_scrollCallback;

    // 静态C风格适配函数,作为GLFW的回调入口
    static void glfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
    static void glfwCursorPosCallback(GLFWwindow* window, double xpos, double ypos);
    static void glfwMouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
    static void glfwScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
};

Window.cpp

#include <glfw/glfw3.h>
#include "GLWindow.h"

GLWindow::GLWindow(const std::string& title, uint32_t width, uint32_t height) {
    handle = glfwCreateWindow(width, height, title.data(), nullptr, nullptr);
    glfwMakeContextCurrent(handle);

    static bool initGLAD = false;
    if (!initGLAD) {
        initGLAD = true;
        gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
    }

    // 将当前GLWindow实例绑定到窗口的用户指针,方便回调中获取
    glfwSetWindowUserPointer(handle, this);
    // 注册静态适配函数给GLFW
    glfwSetKeyCallback(handle, glfwKeyCallback);
    glfwSetCursorPosCallback(handle, glfwCursorPosCallback);
    glfwSetMouseButtonCallback(handle, glfwMouseButtonCallback);
    glfwSetScrollCallback(handle, glfwScrollCallback);
}

GLWindow::~GLWindow() {
    if (handle) {
        glfwDestroyWindow(handle);
    }
}

uint32_t GLWindow::getWidth() const {
    int width;
    glfwGetWindowSize(handle, &width, nullptr);
    return static_cast<uint32_t>(width);
}

uint32_t GLWindow::getHeight() const {
    int height;
    glfwGetWindowSize(handle, nullptr, &height);
    return static_cast<uint32_t>(height);
}

void GLWindow::setKeyCallback(const KeyCallback& callback) {
    m_keyCallback = callback;
}

void GLWindow::setCursorPosCallback(const CursorPosCallback& callback) {
    m_cursorPosCallback = callback;
}

void GLWindow::setMouseCallback(const MouseCallback& callback) {
    m_mouseCallback = callback;
}

void GLWindow::setScrollCallback(const ScrollCallback& callback) {
    m_scrollCallback = callback;
}

GLFWwindow* GLWindow::getGLFWHandle() const {
    return handle;
}

// 静态适配函数:转换GLFW参数为自定义枚举,调用用户回调
void GLWindow::glfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    auto* glWindow = static_cast<GLWindow*>(glfwGetWindowUserPointer(window));
    if (glWindow && glWindow->m_keyCallback) {
        KeyCode keyEnum = static_cast<KeyCode>(key);
        Action actionEnum = static_cast<Action>(action);
        Modifier modEnum = static_cast<Modifier>(mods);
        glWindow->m_keyCallback(keyEnum, actionEnum, modEnum);
    }
}

void GLWindow::glfwCursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
    auto* glWindow = static_cast<GLWindow*>(glfwGetWindowUserPointer(window));
    if (glWindow && glWindow->m_cursorPosCallback) {
        glWindow->m_cursorPosCallback(xpos, ypos);
    }
}

void GLWindow::glfwMouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
    auto* glWindow = static_cast<GLWindow*>(glfwGetWindowUserPointer(window));
    if (glWindow && glWindow->m_mouseCallback) {
        ButtonCode btnEnum = static_cast<ButtonCode>(button);
        Action actionEnum = static_cast<Action>(action);
        Modifier modEnum = static_cast<Modifier>(mods);
        double x, y;
        glfwGetCursorPos(window, &x, &y);
        glWindow->m_mouseCallback(btnEnum, actionEnum, modEnum, x, y);
    }
}

void GLWindow::glfwScrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
    auto* glWindow = static_cast<GLWindow*>(glfwGetWindowUserPointer(window));
    if (glWindow && glWindow->m_scrollCallback) {
        glWindow->m_scrollCallback(xoffset, yoffset);
    }
}

Main.cpp

#include <glfw/glfw3.h>
#include "GLWindow.h"
#include <iostream>

void onKeyCallback(KeyCode key, Action action, Modifier mods) {
    // glfwGetKeyName接受int类型的键码,需要转换
    const char* keyName = glfwGetKeyName(static_cast<int>(key), 0);
    if (keyName) {
        std::cout << "Key: " << keyName << " was " 
                  << (action == Action::Press ? "pressed" : 
                      action == Action::Release ? "released" : "repeated") 
                  << std::endl;
    } else {
        std::cout << "Unknown key pressed" << std::endl;
    }
}

int main() {
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    GLWindow window("myWindow", 640, 480);
    window.setKeyCallback(onKeyCallback);

    while (!glfwWindowShouldClose(window.getGLFWHandle())) {
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window.getGLFWHandle());
        glfwPollEvents(); // 用PollEvents而不是WaitEvents,避免窗口无响应
    }

    glfwTerminate();
    return 0;
}

关键细节说明

  • 静态适配函数:GLFW只能接受静态函数或全局函数作为回调,所以我们用静态成员函数作为入口,通过glfwGetWindowUserPointer获取GLWindow实例,进而调用用户设置的std::function回调。
  • 类型转换:因为你的自定义枚举值和GLFW的原始int值完全对应,所以可以直接用static_cast转换,安全可靠。
  • 回调存储:用std::function存储用户的回调,支持任意可调用对象(函数、lambda、绑定对象等),比原始函数指针更灵活。
  • Main.cpp修正glfwGetKeyName的第一个参数是int类型,所以需要把KeyCode转换成int,同时增加了按键状态的判断,输出更准确的信息。

内容的提问来源于stack exchange,提问作者Stepan_Radchenko

火山引擎 最新活动