XRecord does not receive Device events for touchscreen ButtonPress/ButtonRelease events

Merlijn Wajer merlijn at wizzup.org
Wed Jul 1 14:47:27 UTC 2020


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 at entry=0x561a0b0cc698
<EventCallback>, call_data=call_data at 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 at entry=0x561a0c3471c0, count=count at entry=1,
events=events at 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 at entry=0x561a0c0b1860, pWin=pWin at entry=0x561a0c361680,
pEvents=pEvents at entry=0x561a0c2dace0, count=count at entry=1,
filter=filter at entry=4, grab=grab at 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 at entry=0x7ffe4d95b8b0, dev=dev at entry=0x561a0c0b1860,
level=level at entry=CORE, win=win at entry=0x561a0c361680,
child=child at entry=0, grab=grab at entry=0x0)
    at ../../../../dix/events.c:2681
#10 0x0000561a0aecf151 in DeliverDeviceEvents
    (pWin=0x561a0c361680, event=event at entry=0x7ffe4d95b8b0,
grab=grab at entry=0x0, stopAt=stopAt at entry=0x561a0c361680,
dev=dev at entry=0x561a0c0b1860)
    at ../../../../dix/events.c:2739
#11 0x0000561a0afa637a in DeliverTouchEmulatedEvent
    (dev=dev at entry=0x561a0c0b1860, ti=ti at entry=0x561a0c3e85d0,
ev=ev at entry=0x7ffe4d95c6e0, win=win at entry=0x561a0c361680,
grab=grab at 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 at entry=0x561a0c0b1860, ti=ti at entry=0x561a0c3e85d0,
ev=ev at entry=0x7ffe4d95c6e0, win=win at 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 at entry=0x561a0c0b1860, ti=ti at entry=0x561a0c3e85d0,
ev=ev at 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 at entry=0x561a0c4b3580, event=event at entry=0x7ffe4d95d340,
screen=screen at 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 at 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 at entry=0x7ffe4d95d340, device=device at 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 at entry=0x561a0c328430, event=event at entry=0x7ffe4d95d340,
screen=screen at 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();
}


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: OpenPGP digital signature
URL: <https://lists.x.org/archives/xorg-devel/attachments/20200701/23607819/attachment.sig>


More information about the xorg-devel mailing list