C语言 计数鼠标按钮X11 Linux

ctehm74n  于 12个月前  发布在  Linux
关注(0)|答案(1)|浏览(210)

这是我的第一个问题,但我真的迷路了,我需要你的帮助.我写了一个程序在C,打印出鼠标按钮的数量.我决定使用XI. h为此事.该程序的行为奇怪.
这里展示的代码是一个更大程序的代码,这就是为什么包含了这么多库的原因。只是不要看它。
使用的源:XListInputDevicesXInternAtom
程序代码(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驱动程序有关,但我不确定

xxls0lw8

xxls0lw81#

我知道你可能期待一个实际的答案,但在我们谈论经验方面之前,我们需要讨论一些先验方面,一些哲学和概念问题。
我们把你的鼠标作为物理现实的一部分,这是一件事。软件不是现实本身,而是一个代表现实的模型。作为一个模型,总是有局限性,不完美等等。
所以,适当的问题是:你想知道你的实际物理设备的按钮的数量,或者Xorg识别的按钮的数量?如果你想获得实际物理设备的按钮的数量,你需要抓住它并计数它。
Xorg库将为您提供有关这些库所看到的信息。与某些专有操作系统不同,Xorg以及Unix(&类)系统上的大多数东西通常依赖于更通用的驱动程序。因此,一些Xorg驱动程序将被调整以支持大量设备,使用大量操作系统驱动程序-因为它也可以在多个操作系统上工作。(PS:我不是Xorg或Xorg驱动程序的Maven,所以如果有人可以在这里提供更准确的细节,欢迎这样做,并删除此说明后)
是的,我们的堆栈在这里还有另一层,操作系统,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实现,使用XInput 2。虽然,我建议使用你的操作系统中的一些东西,如果不是这样的话,这在很大程度上取决于你的需求和建模用户是什么。
无关评论
在Wayland上,我得到了一个Segmentation Fault,因为你使用了很多未初始化的指针。永远不要在C中这样做,总是初始化你的指针,否则它们将以内存垃圾为值,而内存垃圾值指针指向未定义的位置。

相关问题