:: [maemo-leste] XRecord does not rece…
Top Page
Delete this message
Reply to this message
Author: Merlijn Wajer
Date:  
To: xorg-devel
CC: maemo-leste, Ivaylo Dimitrov
Subject: [maemo-leste] XRecord does not receive Device events for touchscreen ButtonPress/ButtonRelease events
Hi,

I have written a program to capture ButtonPress and KeyPress events, to
play sounds on KeyPress and ButtonPress, and vibrate a device (mobile
phone) on ButtonPress. This works fine with a mouse and keyboard, but
fails with a touchscreen: the ButtonPress events are never delivered.

The problem seems to be that the touchscreen ButtonPress events are
delivered to record.c's RecordADeliveredEventOrError, where the normal
device events (from mouse, keyboard) get delivered to RecordADeviceEvent.

So if X Record using program only asks for device events (which mine
does), it doesn't receive the touchscreen ButtonPress events at all.

This seems like a bug to me, which can be worked around by asking for
any other (non-device event), like so:

>     ranges[0]->device_events.first = KeyPress;
>     ranges[0]->device_events.last = KeyPress;
>     ranges[1]->device_events.first = ButtonPress;
>     ranges[1]->device_events.last = ButtonPress;
> #if WORKAROUND
>     ranges[2]->errors.first = 0;
>     ranges[2]->errors.last = 100;
> #endif


When these XRecordRanges are presented (as opposed to just the first
two), then the ButtonPress events from touchscreen are also sent to the
X Record callback. But this is not a good workound either: many other
events (like MotionNotify from the touchscreen) are also sent along,
since those are also erroneously (?) sent through
RecordADeliveredEventOrError.

I don't have a lot of knowledge of the internals of X, but it seems that
for events to end up at RecordADeviceEvent, they need to queued for the
DeviceEventCallback (Xi/exevents.c), not for the EventCallback list.

From the backtrace from a touchscreen event [1], it looks like
dix/events.c still has the right intention from function
DeliverDeviceEvents (as implied by the name), but somewhere things go
south: ultimately WriteEventsToClient just calls the EventCallback list,
regardless of the event type. TryClientEvents doesn't seem to use the
source input device for any logic other than repeated key press /
release fixups.

Does anyone here have a clue what's up here, and what the right way to
fix this would be? I'd like to contribute a fix, but I think I could use
some guidance / feedback here.

The simple change to me seems to simply fix up record.c to check for
device events in RecordADeliveredEventOrError and deliver (and filter)
them properly, but maybe there is a bigger problem.

Also included a backtrace from a regular ButtonPress from a mouse [2],
and a test program I wrote [3] (compile with `gcc xrecord-test.c -o
xrecord-test $(pkg-config --cflags --libs x11 xext xtst`).

All backtraces are from a X server on current debian stable: 1.20.4

Regards,
Merlijn

[1]

Thread 1 "Xorg" hit Breakpoint 2, RecordADeliveredEventOrError
(pcbl=<optimized out>, nulldata=<optimized out>, calldata=0x7ffe4d95aa10)
    at ../../../../record/record.c:667
667 in ../../../../record/record.c
(gdb) bt
#0  0x0000561a0af74fc0 in RecordADeliveredEventOrError (pcbl=<optimized
out>, nulldata=<optimized out>, calldata=0x7ffe4d95aa10) at
../../../../record/record.c:667
#1  0x0000561a0aec4c94 in _CallCallbacks (pcbl=pcbl@entry=0x561a0b0cc698
<EventCallback>, call_data=call_data@entry=0x7ffe4d95aa10) at
../../../../dix/dixutils.c:737
#2  0x0000561a0aecad5f in CallCallbacks (call_data=0x7ffe4d95aa10,
pcbl=0x561a0b0cc698 <EventCallback>) at ../../../../include/callback.h:83
#3  0x0000561a0aecad5f in WriteEventsToClient
(pClient=pClient@entry=0x561a0c3471c0, count=count@entry=1,
events=events@entry=0x561a0c2dace0)
    at ../../../../dix/events.c:5958
#4  0x0000561a0aecb084 in TryClientEvents
    (filter=<optimized out>, grab=0x0, mask=<optimized out>, count=1,
pEvents=0x561a0c2dace0, dev=<optimized out>, client=0x561a0c3471c0)
    at ../../../../dix/events.c:2021
#5  0x0000561a0aecb084 in TryClientEvents
    (client=0x561a0c3471c0, dev=<optimized out>, pEvents=0x561a0c2dace0,
count=1, mask=<optimized out>, filter=<optimized out>, grab=0x0)
    at ../../../../dix/events.c:1922
#6  0x0000561a0aecebe0 in DeliverToWindowOwner
    (grab=<optimized out>, filter=<optimized out>, count=<optimized
out>, events=<optimized out>, win=<optimized out>, dev=<optimized out>)
    at ../../../../dix/events.c:2091
#7  0x0000561a0aecebe0 in DeliverEventsToWindow
    (pDev=pDev@entry=0x561a0c0b1860, pWin=pWin@entry=0x561a0c361680,
pEvents=pEvents@entry=0x561a0c2dace0, count=count@entry=1,
filter=filter@entry=4, grab=grab@entry=0x0) at ../../../../dix/events.c:2254
#8  0x0000561a0aecf00c in DeliverEvent (grab=0x0, child=0,
win=0x561a0c361680, count=1, xE=0x561a0c2dace0, dev=0x561a0c0b1860) at
../../../../dix/events.c:2649
#9  0x0000561a0aecf00c in DeliverOneEvent
    (event=event@entry=0x7ffe4d95b8b0, dev=dev@entry=0x561a0c0b1860,
level=level@entry=CORE, win=win@entry=0x561a0c361680,
child=child@entry=0, grab=grab@entry=0x0)
    at ../../../../dix/events.c:2681
#10 0x0000561a0aecf151 in DeliverDeviceEvents
    (pWin=0x561a0c361680, event=event@entry=0x7ffe4d95b8b0,
grab=grab@entry=0x0, stopAt=stopAt@entry=0x561a0c361680,
dev=dev@entry=0x561a0c0b1860)
    at ../../../../dix/events.c:2739
#11 0x0000561a0afa637a in DeliverTouchEmulatedEvent
    (dev=dev@entry=0x561a0c0b1860, ti=ti@entry=0x561a0c3e85d0,
ev=ev@entry=0x7ffe4d95c6e0, win=win@entry=0x561a0c361680,
grab=grab@entry=0x0, xi2mask=<optimized out>, client=<optimized out>,
listener=<optimized out>, listener=<optimized out>) at
../../../../Xi/exevents.c:1437
#12 0x0000561a0afa6585 in DeliverTouchEmulatedEvent
    (dev=dev@entry=0x561a0c0b1860, ti=ti@entry=0x561a0c3e85d0,
ev=ev@entry=0x7ffe4d95c6e0, win=win@entry=0x561a0c361680, grab=0x0,
xi2mask=<optimized out>, client=0x561a0c3471c0, listener=<optimized
out>, listener=<optimized out>) at ../../../../Xi/exevents.c:1382
#13 0x0000561a0afa5f12 in DeliverTouchBeginEvent
    (xi2mask=<optimized out>, grab=0x0, win=0x561a0c361680,
client=0x561a0c3471c0, listener=0x561a0ce003e0, ev=0x7ffe4d95c6e0,
ti=0x561a0c3e85d0, dev=0x561a0c0b1860)
    at ../../../../Xi/exevents.c:1890
#14 0x0000561a0afa5f12 in DeliverTouchEvent
    (xi2mask=<optimized out>, grab=0x0, win=0x561a0c361680,
client=0x561a0c3471c0, listener=0x561a0ce003e0, ev=0x7ffe4d95c6e0,
ti=0x561a0c3e85d0, dev=0x561a0c0b1860)
    at ../../../../Xi/exevents.c:2017
#15 0x0000561a0afa5f12 in DeliverTouchEvents
(dev=dev@entry=0x561a0c0b1860, ti=ti@entry=0x561a0c3e85d0,
ev=ev@entry=0x7ffe4d95c6e0, resource=0)
    at ../../../../Xi/exevents.c:2072
#16 0x0000561a0afa8f64 in ProcessTouchEvent (dev=0x561a0c0b1860,
ev=0x7ffe4d95c6e0) at ../../../../Xi/exevents.c:1626
#17 0x0000561a0afa8f64 in ProcessOtherEvent (ev=0x7ffe4d95c6e0,
device=0x561a0c0b1860) at ../../../../Xi/exevents.c:1861
#18 0x0000561a0afcbc47 in ProcessPointerEvent (ev=0x7ffe4d95c6e0,
mouse=0x561a0c0b1860) at ../../../../xkb/xkbAccessX.c:756
#19 0x0000561a0affdf45 in mieqProcessDeviceEvent
(dev=dev@entry=0x561a0c4b3580, event=event@entry=0x7ffe4d95d340,
screen=screen@entry=0x561a0bff4e30)
    at ../../../../mi/mieq.c:496
#20 0x0000561a0affe089 in mieqProcessInputEvents () at
../../../../mi/mieq.c:551
#21 0x0000561a0aefc559 in ProcessInputEvents () at
../../../../../../hw/xfree86/common/xf86Events.c:151
#22 0x0000561a0aebf738 in Dispatch () at ../../../../dix/dispatch.c:417
#23 0x0000561a0aec3986 in dix_main (argc=11, argv=0x7ffe4d95e158,
envp=<optimized out>) at ../../../../dix/main.c:276
#24 0x00007f85b324909b in __libc_start_main (main=
    0x561a0aead640 <main>, argc=11, argv=0x7ffe4d95e158, init=<optimized
out>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7ffe4d95e148)
    at ../csu/libc-start.c:308
#25 0x0000561a0aead67a in _start () at
../../../../../../hw/xfree86/common/xf86Events.c:626


[2]

(gdb) bt
#0  0x0000561a0af7567e in RecordADeviceEvent (pcbl=0x561a0b0d1700
<DeviceEventCallback>, nulldata=0x0, calldata=0x7ffe4d95b9f0) at
../../../../record/record.c:775
#1  0x0000561a0aec4c94 in _CallCallbacks (pcbl=0x561a0b0d1700
<DeviceEventCallback>, call_data=call_data@entry=0x7ffe4d95b9f0) at
../../../../dix/dixutils.c:737
#2  0x0000561a0afa8781 in CallCallbacks (call_data=0x7ffe4d95b9f0,
pcbl=<optimized out>) at ../../../../include/callback.h:83
#3  0x0000561a0afa8781 in ProcessDeviceEvent
(ev=ev@entry=0x7ffe4d95d340, device=device@entry=0x561a0c328430) at
../../../../Xi/exevents.c:1759
#4  0x0000561a0afa8e63 in ProcessOtherEvent (ev=0x7ffe4d95d340,
device=0x561a0c328430) at ../../../../Xi/exevents.c:1873
#5  0x0000561a0afd2875 in ProcessKeyboardEvent (ev=<optimized out>,
keybd=0x561a0c328430) at ../../../../xkb/xkbPrKeyEv.c:178
#6  0x0000561a0affdf2b in mieqProcessDeviceEvent
(dev=dev@entry=0x561a0c328430, event=event@entry=0x7ffe4d95d340,
screen=screen@entry=0x561a0bff4e30)
    at ../../../../mi/mieq.c:491
#7  0x0000561a0affe089 in mieqProcessInputEvents () at
../../../../mi/mieq.c:551
#8  0x0000561a0aefc559 in ProcessInputEvents () at
../../../../../../hw/xfree86/common/xf86Events.c:151
#9  0x0000561a0aebf738 in Dispatch () at ../../../../dix/dispatch.c:417
#10 0x0000561a0aec3986 in dix_main (argc=11, argv=0x7ffe4d95e158,
envp=<optimized out>) at ../../../../dix/main.c:276
#11 0x00007f85b324909b in __libc_start_main (main=
    0x561a0aead640 <main>, argc=11, argv=0x7ffe4d95e158, init=<optimized
out>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7ffe4d95e148)
    at ../csu/libc-start.c:308
#12 0x0000561a0aead67a in _start () at
../../../../../../hw/xfree86/common/xf86Events.c:626


[3]

#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/record.h>

static int verbose = 1;

#define LOG_ERROR(msg) fprintf(stderr, "%s:%u, %s(): " msg "\n",
__FILE__, __LINE__, __FUNCTION__);
#define LOG_ERROR1(fmt, ...) fprintf(stderr, "%s:%u, %s(): " fmt "\n",
__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);
#define LOG_VERBOSE(msg) \
{ \
    if (verbose) { \
        fprintf(stderr, "%s:%u, %s(): " msg "\n", __FILE__, __LINE__,
__FUNCTION__); \
    } \
}
#define LOG_VERBOSE1(fmt, ...) \
{ \
    if (verbose) { \
        fprintf(stderr, "%s:%u, %s(): " fmt "\n", __FILE__, __LINE__,
__FUNCTION__, __VA_ARGS__); \
    } \
}


int xerror_handler(Display * display, XErrorEvent * ev) {
    (void)display;
    (void)ev;


    char buf[512];


    XGetErrorText(display, ev->error_code, &buf, 512);
    LOG_ERROR1("X11 error_handler fired: %s", buf);


    return 0;
}


void xrec_data_cb(XPointer data, XRecordInterceptData * recdat) {
    int diff_ms;
    int keyev, val;
    int device_state;


    unsigned char *xrd = recdat->data;


    if (!xrd) {
        LOG_ERROR("xrd == NULL");
        return;
    }


    LOG_VERBOSE("xrec_data_cb");


    keyev = xrd[0];
    val = xrd[1];


    if (keyev == ButtonPress && verbose) {
        LOG_VERBOSE1("X ButtonPress %d\n", val);
    }
#if 0
    if (keyev == MotionNotify && verbose) {
        LOG_VERBOSE1("X MotionNotify %d\n", val);
    }
#endif
    if (keyev == KeyPress && verbose) {
        LOG_VERBOSE1("X KeyPress %d\n", val);
    }


    XRecordFreeData(recdat);
    return;
}


void *xrec_thread() {
    Display *display;
    XRecordContext recordcontext;
    int major, minor;
#if 1
    XRecordRange *ranges[3];
#else
    XRecordRange *ranges[2];
#endif
    XRecordClientSpec spec;


    display = XOpenDisplay(NULL);
    if (!display) {
        fprintf(stderr, "xrec_thread failed to open display\n");
        exit(EXIT_FAILURE);
    }


    XSetErrorHandler(xerror_handler);
    if (!XRecordQueryVersion(display, &major, &minor)) {
        LOG_ERROR("X Record Extension not available.");
        exit(1);
    }


    LOG_VERBOSE1("X Record %d.%d is available\n", major, minor);


    ranges[0] = XRecordAllocRange();
    ranges[1] = XRecordAllocRange();
#if 1
    ranges[2] = XRecordAllocRange();
#endif


#if 1
    if (!ranges[0] || !ranges[1] || !ranges[2]) {
#else
    if (!ranges[0] || !ranges[1]) {
#endif
        LOG_ERROR("failed to allocate X Record Range");
    }


    ranges[0]->device_events.first = KeyPress;
    ranges[0]->device_events.last = KeyPress;
    ranges[1]->device_events.first = ButtonPress;
    ranges[1]->device_events.last = ButtonPress;
#if 1
    ranges[2]->errors.first = 0;
    ranges[2]->errors.last = 100;
    //ranges[2]->device_events.first = ButtonPress;
    //ranges[2]->device_events.last = ButtonPress;
#endif
    spec = XRecordAllClients;


#if 1
    recordcontext = XRecordCreateContext(display, 0, &spec, 1, ranges, 3);
#else
    recordcontext = XRecordCreateContext(display, 0, &spec, 1, ranges, 2);
#endif
    if (!recordcontext) {
        LOG_ERROR("failed to create X Record Context");
        exit(1);
    }


    if (!XRecordEnableContext(display, recordcontext, xrec_data_cb, NULL)) {
        LOG_ERROR("failed to enable async X record data transfers");
    }


    LOG_VERBOSE("event record finished");


    XFree(ranges[0]);
    XFree(ranges[1]);
#if 1
    XFree(ranges[2]);
#endif


    return NULL;
}


int main(int argc, char **argv) {
    xrec_thread();
}