This is my first question, but I am really lost and I need your help. I am writing a program in C, that prints out amount of buttons a mouse. I decided to use XI.h for this matter. The program behaves strangely.
The code presented is a code of a larger program, that's why so many libs are included. Just don't look at it.
Sources used: XListInputDevices XInternAtom
Code of the program (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);
}
Compile
cc mouse_test.c -lX11 -lXfixes -lXi -o mouse_test
I expect to get the amount of buttons on my mouse(which is 7), but I always get:
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
I think, this maybe related to linux drivers, but I am not sure
I know you're likely expecting a practical answer, but before we talk about the empirical aspects we need to discuss some a priori aspects, some philosophical and conceptual matters.
We have your mouse as part of the physical reality, and this is one thing. Software is not the reality itself, but a model to represent that reality. And as a model, there are always limitations, imperfections, etc.
So, the appropriate question is: do you want to know the number of buttons of your actual physical device, or the number of buttons recognized by Xorg? If you would like to obtain the number of buttons of the actual physical device, you need to grab it and count it.
The Xorg libraries will provide you with information about what those libraries see. And unlike in some proprietary operating systems, Xorg as well as most things on Unix(&-like) systems usually relies on more generic drivers. So some Xorg drivers will be adapted to support a large number of devices, using a large number of OS drivers -- since it works on several operating systems as well. (PS: I am no expert on Xorg or Xorg drivers, so if someone could provide more accurate details here, be welcome to do it and remove this note later)
And yes, our stack has another layer here, the operating system, and Xorg drivers might rely a lot on the OS drivers (in your case, the linux kernel drivers, since I assume you're running Linux). So these drivers might implement all your device's capabilities or might not, and they might or might not offer Xorg accurate information about your actual device.
So, as you can see, we have a whole complexity in our stack that might help you getting inaccurate information.
Furthermore, we have the X11 protocol, which is a legacy protocol from the mid 80s, and Xorg's implementation of it, and the libraries and extensions we are running as well as limiting factors to help you obtain accurate information on the device. We need to consider that we're dealing with a very old stack, too complex and developed with very old desktops in mind, that's been adapted, designed and redesigned to make modern hardware of modern desktops fit in old ideas.
But enough with background, let's go to the solutions.
Like I said, Xorg is a messy framework. We have
XInputAPI, and it's old, and we have something a little bit more accurate and recent that isXInput2. Your code usesXInput(1), so that might be a limiting factor. In fact, my own mouse with a lot of buttons is listed exactly the same way as yours (and so is my touchpad). I made an adapted version of your original code, because I was trying to run on XWayland (I no longer use X natively), but in the end I configured Xorg for another user to test it natively on X.Your code has other issues unrelated to the question that I'll explain in the end, but here what I used:
My output, stripping out non-mice entries:
As you can see, it's either a big coincidence that both our mices all have the same number of buttons and keys, or
XInput(1) is not providing accurate information anymore. To test whether it's a problem with the code, I ranxinput list --longwhere I obtained the following:So, if
xinput listcould retrieve the information appropriately, I looked into that and figured out it was aXInput1 problem. So I tried withXInput2, with the code below:With this code I got a more accurate (I think) result:
So, I think the answer to what I assume to be your question is: Use XInput2, if you really need this information to come from your X11 implementation. Although, I would suggest to use something from your operating system if this is not the case, it depends a lot on what your requirements and modeled user are.
UNRELATED COMMENTS
On Wayland, I was getting a Segmentation Fault because you're using lots of uninitialized pointers. Never do that in C, always initialize your pointers, otherwise they'll have memory garbage as value, and memory garbage valued pointers point to undefined locations.