这是我的第一个问题,但我真的很迷茫,我需要你的帮助。我正在用 C 编写一个程序,打印出鼠标按钮的数量。我决定使用 XI.h 来解决这个问题。该程序的行为很奇怪。
所提供的代码是一个较大程序的代码,这就是包含如此多库的原因。只是别看它。
使用的来源: XListInputDevices XInternAtom
程序代码(mouse.c)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XI.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> /* socket() connect() bind() listen() accept() socketpair() */
#include <sys/types.h>
#include <unistd.h>
#include <string.h> /* strcpy() */
#include <stdio.h> /* perror() */
#include <errno.h> /* error numbers */
#include <stdlib.h>
int main ( void )
{
XDeviceInfo mouse_info;
XDeviceInfoPtr ptr_mouse_info;
XDevice *mouse;
XID mouse_id;
XButtonInfoPtr ptr_mouse_buttons;
XKeyInfoPtr ptr_mouse_keys;
XValuatorInfoPtr ptr_mouse_axes;
char * mouse_name;
int num_devices, num_props, i;
Display *display = XOpenDisplay(NULL);
if (display == NULL)
{
perror("XOpenDisplay error");
exit(-1);
}
ptr_mouse_info = XListInputDevices(display, &num_devices);
for (i = 0; i < num_devices; i++)
{
if (XInternAtom(display, XI_MOUSE, 1) == ptr_mouse_info[i].type)
{
mouse_id=ptr_mouse_info[i].id;
mouse_name=ptr_mouse_info[i].name;
mouse = XOpenDevice(display, mouse_id);
ptr_mouse_buttons = (XButtonInfoPtr)&ptr_mouse_info[i].inputclassinfo[ButtonClass];
ptr_mouse_keys = (XKeyInfoPtr) &ptr_mouse_info[i].inputclassinfo[KeyClass];
printf("Mouse name: %s\n", mouse_name);
printf("Number of buttons: %d\n", ptr_mouse_buttons->num_buttons);
printf("Number of keys: %d\n", ptr_mouse_keys->num_keys);
}
}
printf("Mouse name: %s\n", mouse_name);
printf("Number of buttons: %d\n", ptr_mouse_buttons->num_buttons);
printf("Number of keys: %d\n", ptr_mouse_keys->num_keys);
XFreeDeviceList(ptr_mouse_info);
exit(0);
}
编译
cc mouse_test.c -lX11 -lXfixes -lXi -o mouse_test
我希望获得鼠标上按钮的数量(7 个),但我总是得到:
Mouse name: Logitech Mechanical keyboard Logitech Mechanical keyboard Keyboard
Number of buttons: 4
Number of keys: 2
Mouse name: ROCCAT ROCCAT Kone Pro Keyboard
Number of buttons: 4
Number of keys: 2
Mouse name: ROCCAT ROCCAT Kone Pro
Number of buttons: 4
Number of keys: 2
Mouse name: ROCCAT ROCCAT Kone Pro
Number of buttons: 4
Number of keys: 2
我想,这可能与linux驱动程序有关,但我不确定
我知道您可能期待一个实际的答案,但在我们讨论实证方面之前,我们需要讨论一些“先验”方面,一些哲学和概念问题。 我们将您的鼠标作为物理现实的一部分,这是一回事。软件并不是现实本身,而是代表现实的模型。而作为一个模型,总有局限性、不完美等等。
因此,适当的问题是:您想知道实际物理设备的按钮数量,还是 Xorg 识别的按钮数量?如果你想获取实际物理设备的按钮数量,你需要抓取并数一下。
Xorg 库将为您提供有关这些库所见内容的信息。与某些专有操作系统不同,Xorg 以及 Unix(类似)系统上的大多数东西通常依赖于更通用的驱动程序。因此,一些 Xorg 驱动程序将进行调整,以支持大量设备,使用大量操作系统驱动程序——因为它也可以在多个操作系统上运行。 (PS:我不是 Xorg 或 Xorg 驱动程序方面的专家,所以如果有人可以在这里提供更准确的详细信息,欢迎这样做并稍后删除此注释)
是的,我们的堆栈这里还有另一层,即操作系统,并且 Xorg 驱动程序可能很大程度上依赖于操作系统驱动程序(在您的情况下,是 Linux 内核驱动程序,因为我假设您运行的是 Linux)。因此,这些驱动程序可能会实现您设备的所有功能,也可能不会,并且它们可能会也可能不会向 Xorg 提供有关您实际设备的准确信息。
因此,正如您所看到的,我们的堆栈非常复杂,可能会帮助您获得不准确的信息。
此外,我们还有 X11 协议,这是 80 年代中期的遗留协议,以及 Xorg 对其的实现,以及我们正在运行的库和扩展以及限制因素,以帮助您获取设备上的准确信息。我们需要考虑到我们正在处理一个非常古老的堆栈,太复杂并且是在考虑非常古老的桌面的情况下开发的,它已经过调整、设计和重新设计,以使现代桌面的现代硬件适应旧的想法。
背景知识已经足够了,让我们来看看解决方案。
就像我说的,Xorg 是一个混乱的框架。我们有
XInput
API,而且它很旧,我们有一些更准确和最新的东西,那就是
XInput2
。您的代码使用 XInput
(1),因此这可能是一个限制因素。事实上,我自己的带有很多按钮的鼠标的列出方式与您的完全相同(我的触摸板也是如此)。我对你的原始代码做了一个改编版本,因为我试图在 XWayland 上运行(我不再本地使用 X),但最终我为另一个用户配置了 Xorg 以在 X 上本地测试它。您的代码还有与我最后解释的问题无关的其他问题,但这里是我使用的:
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XI.h>
int main (int argc, char **argv, char **envp)
{
XDeviceInfo mouse_info;
XDeviceInfoPtr ptr_mouse_info;
XDevice *mouse = NULL;
XID mouse_id;
XButtonInfoPtr ptr_mouse_buttons;
XKeyInfoPtr ptr_mouse_keys;
XValuatorInfoPtr ptr_mouse_axes;
char * mouse_name = NULL;
int num_devices, num_props, i;
Display *display = XOpenDisplay(NULL);
if (display == NULL)
{
perror("XOpenDisplay error");
exit(-1);
}
ptr_mouse_info = XListInputDevices(display, &num_devices);
for (i = 0; i < num_devices; i++)
{
printf("[%d] %s\n", i, ptr_mouse_info[i].name);
if (XInternAtom(display, XI_MOUSE, 1) == ptr_mouse_info[i].type)
{
mouse_id=ptr_mouse_info[i].id;
mouse_name=ptr_mouse_info[i].name;
mouse = XOpenDevice(display, mouse_id);
ptr_mouse_buttons = (XButtonInfoPtr)&ptr_mouse_info[i].inputclassinfo[ButtonClass];
ptr_mouse_keys = (XKeyInfoPtr) &ptr_mouse_info[i].inputclassinfo[KeyClass];
printf("Mouse name: %s\n", mouse_name);
printf("Number of buttons: %d\n", ptr_mouse_buttons->num_buttons);
printf("Number of keys: %d\n", ptr_mouse_keys->num_keys);
}
}
XFreeDeviceList(ptr_mouse_info);
exit(0);
}
我的输出,剔除非小鼠条目:
[7] SYNA7DB5:01 06CB:7DB7 Mouse
Mouse name: SYNA7DB5:01 06CB:7DB7 Mouse
Number of buttons: 4
Number of keys: 2
[12] INSTANT USB GAMING MOUSE
Mouse name: INSTANT USB GAMING MOUSE
Number of buttons: 4
Number of keys: 2
如您所见,我们的两只鼠标都具有相同数量的按钮和按键,这要么是一个很大的巧合,要么是
XInput
(1) 不再提供准确的信息。为了测试这是否是代码的问题,我运行了
xinput list --long
,获得了以下信息:INSTANT USB GAMING MOUSE Keyboard id=16 [slave pointer (2)]
Reporting 7 classes:
Class originated from: 16. Type: XIButtonClass
Buttons supported: 7
Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right"
SYNA7DB5:01 06CB:7DB7 Touchpad id=19 [slave pointer (2)]
Reporting 7 classes:
Class originated from: 19. Type: XIButtonClass
Buttons supported: 7
Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right"
因此,如果
xinput list
能够正确检索信息,我对此进行了研究并发现这是一个
XInput
1 问题。所以我尝试使用 XInput2
,代码如下:#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <errno.h>
#include <stdio.h>
int main() {
int num_buttons;
int num_keys;
int ret = 0;
Display *display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Error opening display");
goto main_err;
}
int opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &opcode, &event, &error)) {
fprintf(stderr, "X Input extension not available.\n");
goto main_err;
}
int num_devices;
XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &num_devices);
if (devices == NULL) {
fprintf(stderr, "XIQueryDevice failed.\n");
goto main_err;
}
for (int i = 0; i < num_devices; i++) {
int use = devices[i].use;
int num_classes = devices[i].num_classes;
if (use == XIMasterPointer || use == XISlavePointer) {
printf("Device %d:\n", devices[i].deviceid);
printf(" Name: %s\n", devices[i].name);
num_buttons = 0;
num_keys = 0;
for (int j = 0; j < num_classes; j++) {
XIAnyClassInfo *class = devices[i].classes[j];
XIButtonClassInfo *btn;
XIKeyClassInfo *key;
if (class->type == XIButtonClass) {
btn = (XIButtonClassInfo*) class;
num_buttons = btn->num_buttons;
} else if (class->type == XIKeyClass) {
key = (XIKeyClassInfo*) class;
num_keys = key->num_keycodes;
}
}
printf(" Buttons: %d\n", num_buttons);
printf(" Keys: %d\n", num_keys);
printf("\n");
}
}
XIFreeDeviceInfo(devices);
XCloseDisplay(display);
goto main_return;
main_err:
ret = errno;
perror("Failure: ");
main_return:
return ret;
}
通过这段代码,我得到了更准确的(我认为)结果:
Device 15:
Name: INSTANT USB GAMING MOUSE
Buttons: 9
Keys: 0
Device 19:
Name: SYNA7DB5:01 06CB:7DB7 Touchpad
Buttons: 7
Keys: 0
因此,我认为我认为您的问题的答案是:如果您确实需要来自 X11 实现的此信息,请使用 XInput2。虽然,如果情况并非如此,我建议使用您操作系统中的某些内容,但这在很大程度上取决于您的要求和建模用户。
不相关的评论
在 Wayland 上,我遇到了分段错误,因为您使用了大量未初始化的指针。永远不要在 C 中这样做,总是初始化你的指针,否则它们将把内存垃圾作为值,并且内存垃圾值指针指向未定义的位置。