xbox one 手柄在ubuntu下的使用和开发

1. 安装dkms

sudo apt-get install dkms

2. 安装xone

xone 是基于Linux内核为使用Xbox One 和 Xbox Series X|S 而开发的. 它作为 xpad 的现代替代品,旨在与微软的游戏输入协议(GIP)兼容。
链接:
xone.
xpad.

2.1 下载源码

git clone https://github.com/medusalix/xone

2.2 安装xone

cd xone
sudo ./install.sh --release

2.3 下载固件

这一步需要联网,安装过程中会自动下载一些文件
sudo xone-get-firmware.sh

3. 手柄测试

该部分转载自文章,不过这篇文章中使用的是xone的旧版本xow在我个人使用过程中遇到了一些bug,最终没有解决,使用xone后顺利完成了

将手柄与适配器配对(这一步不同型号手柄配对方法略有差别)
打开linux终端,运行

sudo jstest /dev/input/js4 
Driver version is 2.1.0.
Joystick (Xbox One Wireless Controller) has 8 axes (X, Y, Z, Rx, Ry, Rz, Hat0X, Hat0Y)
and 11 buttons (BtnA, BtnB, BtnX, BtnY, BtnTL, BtnTR, BtnSelect, BtnStart, BtnMode, BtnThumbL, BtnThumbR).
Testing ... (interrupt to exit)
Axes:  0:     0  1:     0  2:-32767  3:     0  4:     0  5:-32767  6:     0  7:     0 Buttons:  0:off  1:off  2:off  3:off  4:off  5:off  6:off  7:off  8:off  9:off 10:off

注意:在/dev/input下可能会生成js0, js1, js2, js3, js4 5个文件,可以分别使用jstest 测试一下,运行sudo jstest /dev/input/js4 后,操作手柄操作杆查看数据是否有变化。

4. xbox手柄操作数据接收与解析

该部分转载自文章
joystick_xbox.h

#include <linux/input.h>
#include <linux/joystick.h>
#include <string>

class JoystickXBox
{
public:
    JoystickXBox(const std::string &dev_name);
    ~JoystickXBox();
    bool Open();
    void Close();
    bool Read(struct js_event &js);
    unsigned char GetAxes()
    {
        return axes_;
    }
    unsigned char GetButtons()
    {
        return buttons_;
    }
    int GetFd()
    {
   		return fd_;
    }
    void PrintData();
    void ProcessData(const struct js_event &js);

private:
    bool debug_ = false;
    int fd_ = -1;
    std::string dev_name_ = "";
    int version_ = 0x000800;
    char name_[512] = "Unkown";
    unsigned char axes_ = 2;
    unsigned char buttons_ = 2;
    int *axis_ = nullptr;
    char *button_ = nullptr;
};

joystick_xbox.cpp

#include <errno.h>
#include <fcntl.h>
#include <memory>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "joystick_xbox.h"

JoystickXBox::JoystickXBox(const std::string &dev_name) : fd_(-1),dev_name_(dev_name)
{
}

JoystickXBox::~JoystickXBox()
{
    if (axis_)
    {
        delete axis_;
        axis_ = nullptr;
    }

    if (button_)
    {
        delete button_;
        button_ = nullptr;
    }
}

bool JoystickXBox::Open()
{
    int fd = -1;
    if (dev_name_.length() == 0)
    {
        return false;
    }
    // O_NONBLOCK open
    fd = open(dev_name_.c_str(), O_RDONLY | O_NONBLOCK);
    if (fd < 0)
    {
        fd_ = -1;
        printf("JoystickXBox open %s error, %d(%s)\n", dev_name_.c_str(), errno, strerror(errno));
        return false;
    }

    ioctl(fd, JSIOCGVERSION, &version_);
    ioctl(fd, JSIOCGAXES, &axes_);
    ioctl(fd, JSIOCGBUTTONS, &buttons_);
    ioctl(fd, JSIOCGNAME(512), name_);
    printf("JoystickXBox Driver version is %d.%d.%d.\n", version_ >> 16, (version_ >> 8) & 0xff, version_ & 0xff);
    printf("JoystickXBox (%s) has %d axes and %d buttons\n", name_, axes_, buttons_);
    fd_ = fd;
    axis_ = (int *)calloc(axes_, sizeof(int));
    button_ = (char *)calloc(buttons_, sizeof(char));

    return true;
}

void JoystickXBox::Close()
{
    if (fd_ > 0)
    {
        close(fd_);
        fd_ = -1;
    }
}

bool JoystickXBox::Read(struct js_event &js)
{
    int len = -1;
    if (fd_ < 0)
    {
        return false;
    }

    memset(&js, 0, sizeof(js));
    len = read(fd_, &js, sizeof(struct js_event));
    if (len != sizeof(struct js_event))
    {
        printf("JoystickXBox: error reading, %d(%s)\n", errno, strerror(errno));
        return false;
    }

    return true;
}

void JoystickXBox::ProcessData(const struct js_event &js)
{
    JoystickFrame frame;
    int joystick_angular_value = 0;
    int joystick_linear_value = 0;
    int button_angular_value = 0;
    int button_linear_value = 0;

    switch (js.type & ~JS_EVENT_INIT)
    {
    case JS_EVENT_BUTTON:
        button_[js.number] = js.value;
        break;
    case JS_EVENT_AXIS:
        axis_[js.number] = js.value;
        break;
    }
    if (debug_)
    {
        PrintData();
    }
}

void JoystickXBox::PrintData()
{
    if (axes_ && axis_)
    {
        printf("Axes: ");
        for (int i = 0; i < axes_; i++)
        {
            printf("%2d:%6d ", i, axis_[i]);
        }
    }
    if (buttons_ && button_)
    {
        printf("Buttons: ");
        for (int i = 0; i < buttons_; i++)
        {
            printf("%2d:%s ", i, button_[i] ? "on " : "off");
        }
    }
    printf("\n");
    fflush(stdout);
}

int main()
{
	bool ret = false;
    int err_cnt = 0;
    fd_set rfds;
    timeval timeout;
    struct js_event js;
    int fd = -1;
    std::string dev_name = "/dev/input/js4";
    std::unique_ptr<JoystickXBox> joystick_xbox = std::make_unique<JoystickXBox>(dev_name);

    ret = joystick_xbox->Open();
    if (!ret)
    {
        return -1;
    }
    fd = joystick_xbox->GetFd();

    while (1)
    {
        usleep(100);
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        int ret = select(fd + 1, &rfds, NULL, NULL, &timeout);
        if (ret > 0 && FD_ISSET(fd, &rfds))
        {
            ret = joystick_xbox->Read(js);
            if (ret)
            {
                joystick_xbox->ProcessData(js);
            }
        }
    }

    joystick_xbox->Close();

    return 0;	
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Xbox One手柄的HID描述符是一个标准的USB HID设备描述符,用于描述设备的功能和属性。该描述符包含多个子描述符,其中最重要的是输入报告描述符,它定义了手柄发送给主机的数据格式。 输入报告描述符的格式如下: ``` 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x20, // Report ID (32) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x04, // Report Count (4) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x39, // Usage (Hat switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x07, // Logical Maximum (7) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x3B, 0x01, // Physical Maximum (315) 0x65, 0x14, // Unit (Eng Rot:Angular Pos) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) 0x75, 0x01, // Report Size (1) 0x95, 0x01, // Report Count (1) 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (Button 1) 0x29, 0x0A, // Usage Maximum (Button 10) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x0A, // Report Count (10) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0 // End Collection ``` 该描述符定义了一个32字节的输入报告格式,包含了手柄的所有输入数据,包括四个轴(X、Y、Z、Rz)、一个八方向摇杆(Hat switch)和十个按钮。每个轴和摇杆的数据用8位字节表示,取值范围为0-255,每个按钮用1位表示,取值为0或1。 数据包格式为: ``` Byte 0: Report ID (0x20) Byte 1: Buttons (bits 0-7) Byte 2: Buttons (bits 8-15) Byte 3: Left Trigger Byte 4: Right Trigger Byte 5: X-axis (LSB) Byte 6: X-axis (MSB) Byte 7: Y-axis (LSB) Byte 8: Y-axis (MSB) Byte 9: Z-axis (LSB) Byte 10: Z-axis (MSB) Byte 11: Rz-axis (LSB) Byte 12: Rz-axis (MSB) Byte 13: Hat Switch (0-7) ``` 其中,Report ID为0x20,表示这是一个输入报告;Buttons的位表示对应的按钮是否按下;Left Trigger和Right Trigger的值为0-255,表示扳机的压力程度;轴的值按照Little Endian格式存储。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值