Qt5支持手柄

背景

使用Qt5的Gamepad模块支持手柄UI显示和操作。Qt使用的版本是5.15.8。采用的是SDL支持的插件。因为想要支持PS5手柄,但是使用默认的xinputgamepad.dll好像对PS5手柄的支持不太好。

编译和构建

SDL2选用github上官方SDL2项目的最新release包即可

  • 需要工作在VS命令行工具。
  • 需要Qt源码

找到qtsoucecodepath\qtgamepad\src\plugins\gamepads\sdl2目录,该目录下有对应的源码

nmake.exe" -f Makefile.Debug all
nmake 
nmake install

Release也行,改一下就好
产物在plugins\gamepads目录下

使用

使用时也放在可执行文件目录的plugins\gamepads文件夹下就可以,gamepadsplatforms同级,SDL2库文件放在可执行文件目录就好。

如果gamepads目录下有xinputgamepad.dll文件可能会优先选择,但是如果gamepads目录下只有sdl2gamepad.dll的话,就只能选择sdl支持了。

踩坑

Qt5 Gamepad模块无法拿到手柄的详细信息,比如手柄的类型(可以通过vendor id做简单类型区分,也可以通过product id做具体手柄区分)。业务场景是要求区分各个厂家的手柄并做对应的按键UI。所以仅靠Qt5 Gamepad是不够的。所以还需要依靠SDL的能力,要将Gamepad和SDL的GameController连接起来。(原来我以为Qt Gamepad的device id和SDL GameController intex不是同一个值,采用了一种特别别扭的映射方法)

通过阅读SDL插件和Qt Gamepad部分的代码,发现其实SDL GameController的index其实是通过信号传递给了Qt5的Gamepad.

void QSdlGamepadBackend::addController(int index)
{
    char GUID[100];
    SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(index), GUID, 100);
    if (!SDL_IsGameController(index))
        return;

    SDL_GameController *controller = SDL_GameControllerOpen(index);
    if (controller) {
        m_indexForController.insert(index, controller);

        SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);

        int instanceID = SDL_JoystickInstanceID(joystick);
        m_instanceIdForIndex.insert(instanceID, index);

        const char *name = SDL_JoystickName(joystick);

        //qDebug() << "Controller " << index << " added with instanceId: " << instanceID;
        emit gamepadAdded(index);

        if (name)
            emit gamepadNamed(index, QString::fromUtf8(name));
    }
}

gamepadAdded信号把index丢给了Qt上层
Qt则是用这个index当做devciceId了,那说明Qt Gamepad和SDL GameController其实是可以通过这个deviceId链接起来的,那么问题就简单多了。

void QGamepadManagerPrivate::_q_forwardGamepadConnected(int deviceId)
{
    Q_Q(QGamepadManager);
    connectedGamepads.insert(deviceId, QString());
    emit q->gamepadConnected(deviceId);
    emit q->connectedGamepadsChanged();
}

只需要维护一个Qt Gamepad到手柄类型的Map映射即可,当Qt的QGamepadManager收到connectedGamepadsChanged信号时,构造一个QGamepad对象,然后通过Gamepad的deviceId去SDL拿到vendor id。

    QGamepadManager* manager = QGamepadManager::instance();
    QList<int> gamepadList = manager->connectedGamepads();
    SPDLOG_INFO("Connected gamepad count: {}", gamepadList.size());
    
    for (const auto& device_id : gamepadList) {
        SPDLOG_INFO("Connected gamepad deviceId: {}", device_id);
        QGamepad* gamepad = new QGamepad(device_id, this);
        if (gamepad != nullptr) {
            Uint16 vendor = SDL_JoystickGetDeviceVendor(device_id);
            // handle gamepad reflect
        } else {
            continue;
        }
    }

关于Qt6

Gamepad好像目前还没有移植到Qt6,需要找一下其他的替代方案了,可以考虑直接引入SDL,但是不知道会不会和Qt有事件冲突

posted @ 2025-11-19 12:40  leno米雷  阅读(17)  评论(0)    收藏  举报