如何在 Linux 系统中直接处理来自字符 device/gamepad 的输入?
How does one handle input from a character device/gamepad directly in Linux systems?
我正在用 C 语言开发一个程序,它使用 USB SNES controller 作为基于 RPM 的发行版的输入。是否有任何人知道的库可以使它更容易与之交互,或者是否有一些工具(joydev?)允许从设备正确读取输入?我不需要一个完整的游戏引擎;仅供字符设备输入。
如果有一个图书馆已经为我做了这件事,那就太好了(我可以自己看看这个图书馆做了什么)而且这可以用 link 关闭;否则,如果我必须自己做,我有几个具体问题:
问题:
- 是否有一个现有的 C 库可以为我处理我与游戏手柄的所有 USB 设备交互?我很乐意学习一个新的图书馆。 (我的 google-fu 在这里让我失望了,如果这太明显了,我深表歉意)
- 当事件*名称在 sessions/initialization 之间变化时,确保每次打开正确字符设备的适当方法是什么?
- 从我的应用程序处理来自这些设备的输入的适当方法是什么?当我们轮询字符设备时,只需确定每个按钮按下的值,并根据该输入执行操作吗?
简而言之,pseduo-C,是这样的吗?
struct gamepad {
int fd;
};
void get_input(struct gamepad *gamepad)
{
char *buf;
read(gamepad->fd, buf, 48);
switch(buf)
{
/* insert cases about handling differing buttons */
}
}
设备如何显示自身:
就我所见,我可以清楚地看到设备正确注册:
$ dmesg | tail
[198564.517068] usb 1-1: USB disconnect, device number 17
[198566.154324] usb 1-1: new low-speed USB device number 18 using xhci_hcd
[198566.323309] usb 1-1: New USB device found, idVendor=12bd, idProduct=d015
[198566.323312] usb 1-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[198566.323313] usb 1-1: Product: 2Axes 11Keys Game Pad
[198566.323792] usb 1-1: ep 0x81 - rounding interval to 64 microframes, ep desc says 80 microframes
[198566.328692] input: 2Axes 11Keys Game Pad as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/input/input20
[198566.329036] hid-generic 0003:12BD:D015.0006: input,hidraw2: USB HID v1.10 Gamepad [2Axes 11Keys Game Pad] on usb-0000:00:14.0-1/input0
如果我从设备读取,我可以看到它收到一个中断并从设备输入只是使用 hexdump:
$ ls -lattr /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick
lrwxrwxrwx. 1 root root 10 Jan 20 15:56 /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick -> ../event17
当我按下一个键(而不是释放)时,它似乎按预期工作,尽管我无法在没有上下文的情况下破译从缓冲区返回的内容:
$ hexdump /dev/input/event17
0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000
释放按钮时,您会收到类似的输出:
0000030 f53c 569f 0000 0000 8be3 0007 0000 0000
0000040 0003 0001 007f 0000 f53c 569f 0000 0000
0000050 8be3 0007 0000 0000 0000 0000 0000 0000
也就是上面的'up'按钮被按下和松开
研究:
为了确定其他库是如何做到这一点的,我考虑过在 python 中执行 pygame
的 strace 并查看它打开的设备以及它如何读取输入,但我我仍在学习如何使用它。我还看到一些关于 joydev 的模糊提及,但同样,我还没有学会如何使用它们。我目前正在这样做,如果我学到任何有价值的东西,将会 post 结果。
除此之外,通过 ASCII 和 hexdump 观察按钮按下,我注意到它们有类似的基于按钮的输入,但我认为似乎是在 USB 总线结束时的中断计数上面的输出(0xf53a 到 0xf53c)。这似乎也总是在增加,并且为了我的目的,可能会被丢弃。
也有可能我没有正确安装设备,因为我缺少一些模块或包(再次思考 joydev 及其应该做什么)。我根本不经常使用 USB,所以这种类型的设备处理对我来说是新的。
搜索了一段时间,我没有看到任何能准确显示我正在寻找的东西,但我很乐意接受重定向到其他 questions/topics 阅读。
提前致谢!
它是一个 USB Hid 设备,因此它由 hid 驱动程序处理 - 请参阅调试输出 "USB HID v1.10 Gamepad"
有各种教程和示例说明如何操作。
作为起点,您可以在 "apt-get install joystick" 上安装操纵杆并查看源代码。
libhidapi 也是不错的选择。
Linux中的 USB 输入设备通常由 HID 驱动程序(人机接口设备)处理,然后再转换为输入设备。
您可以将它们作为原始 USB 设备读取,但这通常不是一个好主意,因为它是一个非常低级的协议。
如果您有适当的权限,您可以读取 /dev/input/*
设备。通常它们只能由 root 读取。如果您不想读取原始字节,可以使用一些库,例如 libinput,可以为您完成这项工作。
但是如果您的游戏在 XWindows 中运行(最有可能),那么您应该管理 XInput 设备。您可以使用原始 X 调用来做到这一点,但您最好使用一些库,例如 SDL。事实上,SDL 是 pygame
在幕后使用的,所以我会先尝试一下。
关于如何识别正确的设备,每个输入设备都有一个名字,有的甚至有一个序列号(你看那些是/dev/input/by-id
下的符号链接这些通常足以识别设备,而不是输入的数字。
如果您想阅读原始输入设备,让我解释一下您的 hexdumps。您正在读取 input*
设备,因此您正在获取 struct input_event
类型的值(参见 /usr/include/linux/input.h
):
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
在您的第一个转储中,例如:
0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000
其实有两个input_event
。第一个是:
f53a 569f 0000 0000 ac6c 000c 0000 0000 0003 0000 007f 0000
前 64 个字节是时间戳。然后,0003 (EV_ABS)
表示绝对轴的移动,0000 (ABS_X)
是轴索引,0000007f
是该轴的位置。绝对轴有时用于表示油门、操纵杆等(有时键盘作为操纵杆而不是 4 个按钮发送),即使您不移动它,您也会在第一次读取时获得位置以了解控件的位置。
第二个事件是:
f53a 569f 0000 0000 ac6c 000c 0000 0000 0000 0000 0000 0000
前64个字节是时间戳(同上。然后0000 (EV_SYN)
表示同步事件。用EV_SYN
,其他字段不用。EV_SYN
用于分组将单个事件的不同值组合在一起,例如鼠标或操纵杆的水平轴和垂直轴。
您的其他转储类似,但 AXIS_Y
。
我的猜测是小键盘被视为数字操纵杆,两个轴 ABS_X
和 ABS_Y
,中间点 0x7F
(范围从 0x00
0xFF
)。您收到的消息只是中间点,即没有按下键盘按钮。也许您的 hexdump
输出正在缓冲?
我正在用 C 语言开发一个程序,它使用 USB SNES controller 作为基于 RPM 的发行版的输入。是否有任何人知道的库可以使它更容易与之交互,或者是否有一些工具(joydev?)允许从设备正确读取输入?我不需要一个完整的游戏引擎;仅供字符设备输入。
如果有一个图书馆已经为我做了这件事,那就太好了(我可以自己看看这个图书馆做了什么)而且这可以用 link 关闭;否则,如果我必须自己做,我有几个具体问题:
问题:
- 是否有一个现有的 C 库可以为我处理我与游戏手柄的所有 USB 设备交互?我很乐意学习一个新的图书馆。 (我的 google-fu 在这里让我失望了,如果这太明显了,我深表歉意)
- 当事件*名称在 sessions/initialization 之间变化时,确保每次打开正确字符设备的适当方法是什么?
- 从我的应用程序处理来自这些设备的输入的适当方法是什么?当我们轮询字符设备时,只需确定每个按钮按下的值,并根据该输入执行操作吗?
简而言之,pseduo-C,是这样的吗?
struct gamepad {
int fd;
};
void get_input(struct gamepad *gamepad)
{
char *buf;
read(gamepad->fd, buf, 48);
switch(buf)
{
/* insert cases about handling differing buttons */
}
}
设备如何显示自身:
就我所见,我可以清楚地看到设备正确注册:
$ dmesg | tail
[198564.517068] usb 1-1: USB disconnect, device number 17
[198566.154324] usb 1-1: new low-speed USB device number 18 using xhci_hcd
[198566.323309] usb 1-1: New USB device found, idVendor=12bd, idProduct=d015
[198566.323312] usb 1-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[198566.323313] usb 1-1: Product: 2Axes 11Keys Game Pad
[198566.323792] usb 1-1: ep 0x81 - rounding interval to 64 microframes, ep desc says 80 microframes
[198566.328692] input: 2Axes 11Keys Game Pad as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/input/input20
[198566.329036] hid-generic 0003:12BD:D015.0006: input,hidraw2: USB HID v1.10 Gamepad [2Axes 11Keys Game Pad] on usb-0000:00:14.0-1/input0
如果我从设备读取,我可以看到它收到一个中断并从设备输入只是使用 hexdump:
$ ls -lattr /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick
lrwxrwxrwx. 1 root root 10 Jan 20 15:56 /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick -> ../event17
当我按下一个键(而不是释放)时,它似乎按预期工作,尽管我无法在没有上下文的情况下破译从缓冲区返回的内容:
$ hexdump /dev/input/event17
0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000
释放按钮时,您会收到类似的输出:
0000030 f53c 569f 0000 0000 8be3 0007 0000 0000
0000040 0003 0001 007f 0000 f53c 569f 0000 0000
0000050 8be3 0007 0000 0000 0000 0000 0000 0000
也就是上面的'up'按钮被按下和松开
研究:
为了确定其他库是如何做到这一点的,我考虑过在 python 中执行 pygame
的 strace 并查看它打开的设备以及它如何读取输入,但我我仍在学习如何使用它。我还看到一些关于 joydev 的模糊提及,但同样,我还没有学会如何使用它们。我目前正在这样做,如果我学到任何有价值的东西,将会 post 结果。
除此之外,通过 ASCII 和 hexdump 观察按钮按下,我注意到它们有类似的基于按钮的输入,但我认为似乎是在 USB 总线结束时的中断计数上面的输出(0xf53a 到 0xf53c)。这似乎也总是在增加,并且为了我的目的,可能会被丢弃。
也有可能我没有正确安装设备,因为我缺少一些模块或包(再次思考 joydev 及其应该做什么)。我根本不经常使用 USB,所以这种类型的设备处理对我来说是新的。
搜索了一段时间,我没有看到任何能准确显示我正在寻找的东西,但我很乐意接受重定向到其他 questions/topics 阅读。
提前致谢!
它是一个 USB Hid 设备,因此它由 hid 驱动程序处理 - 请参阅调试输出 "USB HID v1.10 Gamepad" 有各种教程和示例说明如何操作。
作为起点,您可以在 "apt-get install joystick" 上安装操纵杆并查看源代码。
libhidapi 也是不错的选择。
Linux中的 USB 输入设备通常由 HID 驱动程序(人机接口设备)处理,然后再转换为输入设备。
您可以将它们作为原始 USB 设备读取,但这通常不是一个好主意,因为它是一个非常低级的协议。
如果您有适当的权限,您可以读取 /dev/input/*
设备。通常它们只能由 root 读取。如果您不想读取原始字节,可以使用一些库,例如 libinput,可以为您完成这项工作。
但是如果您的游戏在 XWindows 中运行(最有可能),那么您应该管理 XInput 设备。您可以使用原始 X 调用来做到这一点,但您最好使用一些库,例如 SDL。事实上,SDL 是 pygame
在幕后使用的,所以我会先尝试一下。
关于如何识别正确的设备,每个输入设备都有一个名字,有的甚至有一个序列号(你看那些是/dev/input/by-id
下的符号链接这些通常足以识别设备,而不是输入的数字。
如果您想阅读原始输入设备,让我解释一下您的 hexdumps。您正在读取 input*
设备,因此您正在获取 struct input_event
类型的值(参见 /usr/include/linux/input.h
):
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
在您的第一个转储中,例如:
0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000
其实有两个input_event
。第一个是:
f53a 569f 0000 0000 ac6c 000c 0000 0000 0003 0000 007f 0000
前 64 个字节是时间戳。然后,0003 (EV_ABS)
表示绝对轴的移动,0000 (ABS_X)
是轴索引,0000007f
是该轴的位置。绝对轴有时用于表示油门、操纵杆等(有时键盘作为操纵杆而不是 4 个按钮发送),即使您不移动它,您也会在第一次读取时获得位置以了解控件的位置。
第二个事件是:
f53a 569f 0000 0000 ac6c 000c 0000 0000 0000 0000 0000 0000
前64个字节是时间戳(同上。然后0000 (EV_SYN)
表示同步事件。用EV_SYN
,其他字段不用。EV_SYN
用于分组将单个事件的不同值组合在一起,例如鼠标或操纵杆的水平轴和垂直轴。
您的其他转储类似,但 AXIS_Y
。
我的猜测是小键盘被视为数字操纵杆,两个轴 ABS_X
和 ABS_Y
,中间点 0x7F
(范围从 0x00
0xFF
)。您收到的消息只是中间点,即没有按下键盘按钮。也许您的 hexdump
输出正在缓冲?