I'm trying to use the X11 library to look at events from a specific device, but getting stuck at XOpenDevice
.
The code:
#include "stdio.h"
#include <stdlib.h>
#include "string.h"
#include "X11/extensions/XInput.h"
#include "X11/Xlib.h"
int main(int argc, char *argv[])
{
Display *dpy = XOpenDisplay(0);
int *device_count;
XDeviceInfo *devices = XListInputDevices(dpy, &device_count);
int mouse_id;
for (int i = 0; i < device_count; i++)
if (0 == strcmp(devices[i].name, "SIGMACHIP SG 2.4G Wireless Mouse"))
mouse_id = devices[i].id;
XFreeDeviceList(devices);
printf("mouse_id: %d\n", mouse_id);
XDevice *mouse = XOpenDevice(&dpy, mouse_id); // <-- segmentation fault
// do something
XCloseDevice(&dpy, mouse);
XCloseDisplay(dpy);
return 0;
}
Compile and run:
gcc -g -fsanitize=address code.c -o prog.exe -lX11 -lXi
./prog.exe
Output:
> gcc -g -fsanitize=address code.c -o prog.exe -lX11 -lXi
code.c: In function ‘main’:
code.c:12:51: warning: passing argument 2 of ‘XListInputDevices’ from incompatible pointer type [-Wincompatible-pointer-types]
12 | XDeviceInfo *devices = XListInputDevices(dpy, &device_count);
| ^~~~~~~~~~~~~
| |
| int **
In file included from code.c:4:
/usr/include/X11/extensions/XInput.h:1133:5: note: expected ‘int *’ but argument is of type ‘int **’
1133 | int* /* ndevices */
| ^~~~
code.c:15:23: warning: comparison between pointer and integer
15 | for (int i = 0; i < device_count; i++)
| ^
code.c:21:34: warning: passing argument 1 of ‘XOpenDevice’ from incompatible pointer type [-Wincompatible-pointer-types]
21 | XDevice *mouse = XOpenDevice(&dpy, mouse_id); // <-- segmentation fault
| ^~~~
| |
| Display **
/usr/include/X11/extensions/XInput.h:1141:5: note: expected ‘Display *’ but argument is of type ‘Display **’
1141 | Display* /* display */,
| ^~~~~~~~
code.c:23:18: warning: passing argument 1 of ‘XCloseDevice’ from incompatible pointer type [-Wincompatible-pointer-types]
23 | XCloseDevice(&dpy, mouse);
| ^~~~
| |
| Display **
/usr/include/X11/extensions/XInput.h:1146:5: note: expected ‘Display *’ but argument is of type ‘Display **’
1146 | Display* /* display */,
| ^~~~~~~~
> ./prog.exe
mouse_id: 10
AddressSanitizer:DEADLYSIGNAL
=================================================================
==28208==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000048 (pc 0x7ffba8d03613 bp 0x000000000008 sp 0x7ffdf9cfb420 T0)
==28208==The signal is caused by a READ memory access.
==28208==Hint: address points to the zero page.
#0 0x7ffba8d03613 in _XFlush (/lib64/libX11.so.6+0x49613) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
#1 0x7ffba8d038e4 in _XGetRequest (/lib64/libX11.so.6+0x498e4) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
#2 0x7ffba8cf565e in XQueryExtension (/lib64/libX11.so.6+0x3b65e) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
#3 0x7ffba951a800 (/lib64/libXi.so.6+0x3800) (BuildId: ee64e5f4d60239d9dcab6326d02bd5c2c6e31259)
#4 0x7ffba9521d68 in XOpenDevice (/lib64/libXi.so.6+0xad68) (BuildId: ee64e5f4d60239d9dcab6326d02bd5c2c6e31259)
#5 0x401430 in main /home/levi/development/mouse-chording/code.c:21
#6 0x7ffba8ae4bef in __libc_start_call_main (/lib64/libc.so.6+0x27bef) (BuildId: 7ed2a863c9bc5e582a36387b1b1f26053e10ef16)
#7 0x7ffba8ae4cb8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x27cb8) (BuildId: 7ed2a863c9bc5e582a36387b1b1f26053e10ef16)
#8 0x401154 in _start ../sysdeps/x86_64/start.S:115
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib64/libX11.so.6+0x49613) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418) in _XFlush
==28208==ABORTING
I tried this, but got the same error:
XDevice *mouse = malloc( sizeof(XDevice) );
mouse = XOpenDevice(&dpy, mouse_id);
I tried changing int mouse_id
to XID mouse_id
to more closely match the API, but same error.
I've looked up repos using the same API, trying to find differences, but it seems like I am doing the same as everybody else. Example: https://github.com/FreeRDP/FreeRDP/blob/10386e73bcebd450062a9ef5272260af5b621d05/client/X11/xf_client.c#L991C1-L991C59
Have barely used C before, so I am not sure where to go from here.
Relevant docs: https://x.org/releases/X11R7.7/doc/libXi/inputlib.html#XOpenDevice
as pointed out by @someprogrammerdude, the problem was a misunderstanding around pointers.
since dpy
was initialized as a pointer with Display *dpy
it did not need the &
to be passed as a pointer (the asterix makes it a pointer).
device_count
had the opposite problem.
and this is what the fixed version looks like.
#include "stdio.h"
#include "string.h"
#include "X11/extensions/XInput.h"
#include "X11/Xlib.h"
int main(int argc, char *argv[])
{
Display *dpy = XOpenDisplay(0);
int device_count;
XDeviceInfo *devices;
devices = XListInputDevices(dpy, &device_count);
int mouse_id;
for (int i = 0; i < device_count; i++)
if (0 == strcmp(devices[i].name, "SIGMACHIP SG 2.4G Wireless Mouse"))
mouse_id = devices[i].id;
XFreeDeviceList(devices);
printf("mouse_id: %d\n", mouse_id);
XDevice *mouse = XOpenDevice(dpy, mouse_id);
// do something
XCloseDevice(dpy, mouse);
XCloseDisplay(dpy);
return 0;
}