-1

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

Levi
  • 661
  • 7
  • 26
  • Aside: `int mouse_id;` is not initialised and so if you don't get a string match that sets it, the code blunders on to undefined behaviour. – Weather Vane Jul 31 '23 at 11:46
  • 4
    I'm surprised that the compiler doesn't warn you about passing a `Display **` argument to a [function](https://www.x.org/releases/current/doc/man/man3/XOpenDevice.3.xhtml) taking only `Display *` argument. – Some programmer dude Jul 31 '23 at 11:47
  • It does warn me, the output is only from the run, not the compilation. Seems the compiler knows to do what I mean though. Will add the rest of the output @Someprogrammerdude – Levi Jul 31 '23 at 11:55
  • 3
    Passing the wrong pointer to a function leads to *undefined behavior*. Try changing `XOpenDevice(&dpy, mouse_id)` to `XOpenDevice(dpy, mouse_id)`. Very much the same with the other pointer errors that the compiler points out. Always treat warnings as actual errors that needs to be fixed. – Some programmer dude Jul 31 '23 at 11:57
  • Yeah, that got rid of the warnings. But I still get a segmentation error, but a different one. Will update. – Levi Jul 31 '23 at 11:59
  • 1
    Regarding `XListInputDevices` and `device_count` it seems you're misunderstanding how *emulating pass by reference* works in C. You're supposed to define a plain `int` variable, not a pointer to an `int`, and then use the pointer-to operator to get the pointer to `int` (of type `int *`). – Some programmer dude Jul 31 '23 at 11:59
  • 4
    Please ask a new question for another problem - don't make this a "rolling code". – Weather Vane Jul 31 '23 at 12:00
  • 1
    I like it, when SO successfully does the mind-reading-trick. Meaning me thinking of a comment and just when I want to start typing it in - it appears already there, for me to just tick the "agree". ;-) @WeatherVane – Yunnosch Jul 31 '23 at 12:01
  • @Someprogrammerdude You're right, I didn't get that until just now. Changed `&dpy` to `dpy` in the calls and the opposite for `device_count` and now it compiles without warning and runs without errors :) – Levi Jul 31 '23 at 12:16
  • 1
    Make sure the program does something sensible if there is no string match: write bullet proof code. – Weather Vane Jul 31 '23 at 12:21
  • 1
    @WeatherVane I will. My "actual" code has error checks for every step, I just stripped it down to make it shorter and quicker to read for SO :) – Levi Jul 31 '23 at 12:28

1 Answers1

1

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;
}
Levi
  • 661
  • 7
  • 26