Bug 54614

Summary: Touch event history replay doesn't end the touch
Product: xorg Reporter: Daniel Drake <dan>
Component: Server/Input/CoreAssignee: Peter Hutterer <peter.hutterer>
Status: RESOLVED WONTFIX QA Contact: Xorg Project Team <xorg-team>
Severity: normal    
Priority: medium CC: chase.douglas, peter.hutterer
Version: unspecified   
Hardware: Other   
OS: All   
Whiteboard:
i915 platform: i915 features:
Attachments:
Description Flags
gdb output illustrating the issue
none
xinput test-xi2 output showing spurious TouchBegin none

Description Daniel Drake 2012-09-06 20:57:53 UTC
Created attachment 66748 [details]
gdb output illustrating the issue

I'm working with a touch driver which operates a "type A" device according to the documentation at http://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt

The touch events sent at the evdev level are as documented there, e.g. for a single touch:

   ABS_MT_POSITION_X x[1]
   ABS_MT_POSITION_Y y[1]
   SYN_MT_REPORT
   SYN_REPORT

and release:

   SYN_MT_REPORT
   SYN_REPORT

xf86-input-evdev (v2.7.3) interprets this correctly and sends XI_TouchBegin and XI_TouchEnd as expected.

But beyond that, the X server (v1.12.99.905) is getting confused. "xinput test-xi2" shows that for a simple touch, a spurious TouchBegin is generated at the end of the sequence of events generated for a quick touch-and-release.

Digging into the code, looking with gdb, I can see that the sequence of events is something like this:

 1. DeliverTouchEvents delivers an ET_TouchBegin
 2. DeliverTouchEvents delivers another ET_TouchBegin
 3. DeliverTouchEvents delivers an ET_TouchUpdate
 4. DeliverTouchEvents delivers an ET_TouchEnd
 5. TouchEndTouch marks the touch as pending_finish=FALSE
 6. DeliverTouchEvents delivers another ET_TouchEnd
 7. TouchPuntToNextOwner replays the touch history, generating an ET_TouchBegin but without generating a corresponding ET_TouchEnd.

The code in TouchPuntToNextOwner() that should generate a TouchEnd at this point is:
    /* If we've just removed the last grab and the touch has physically
     * ended, send a TouchEnd event too and finalise the touch. */
    if (ti->num_listeners == 1 && ti->num_grabs == 0 && ti->pending_finish) {
        EmitTouchEnd(dev, ti, 0, 0);
        TouchEndTouch(dev, ti);
        return;
    }

However, pending_finish was set to FALSE quite a bit earlier (#5 above), so this doesn't trigger.

So, there is some kind of error here, perhaps calling TouchEndTouch too early, or maybe pending_finish should not be unset by TouchEndTouch but should instead be unset some time later.

I'm attaching the test-xi2 output showing the spurious touch, and a gdb session showing backtraces at all of the above events.
Comment 1 Daniel Drake 2012-09-06 21:02:14 UTC
Created attachment 66749 [details]
xinput test-xi2 output showing spurious TouchBegin
Comment 2 Daniel Drake 2012-09-06 21:04:12 UTC
Forgot to mention, I believe this spurious touch is leading to the real issue in question, which is where after touching (and releasing) the screen for the first time, moving the mouse around the screen (with the touchpad) acts as if the first moust button is held down - its not.
Comment 3 Daniel Drake 2012-09-07 21:01:02 UTC
Digging further, maybe that is not the issue. I now understand that there are effectively two touches tracked for when I touch the screen (one on the touchscreen device, one on the core pointer). So it is OK if one of the touches is destroyed with TouchEndTouch() if the other touch is still alive and can carry the appropriate flags to generate the appropriate sequence of events.

So, in more detail, here is what is happening, in response to a quick tap-and-release:

evdev sends XI_TouchBegin as my finger touches the screen.

ProcessTouchEvent() is called with ET_TouchBegin for the touchscreen device.
DeliverTouchEvents calls DeliverTouchEvent, things look OK.

ProcessTouchEvent() is called with ET_TouchBegin for the Virtual core pointer
DeliverTouchEvents calls DeliverTouchEvent, things look OK.

evdev sends XI_TouchEnd as my finger leaves the screen.

ProcessTouchEvent() is called with ET_TouchEnd for the touchscreen device.
DeliverTouchEvents calls DeliverTouchEvent, things look OK. TouchEndTouch() is called. So the touch relating to the touchscreen device is now complete - all events have been reported correctly and now it has been destroyed. We're just left with handling the completion of the other touch:

ProcessTouchEvent() is called with ET_TouchEnd for the Virtual core pointer.
DeliverTouchEvents calls DeliverTouchEvent.
We reach DeliverTouchEndEvent(), with listener type  LISTENER_POINTER_GRAB and state LISTENER_IS_OWNER. So we call into DeliverTouchEmulatedEvent().

Presumably due to DeliverTouchEmulatedEvent() (haven't examined exactly what this function does), we end up in ProcessTouchEvent() for a ET_TouchOwnership event for the virtual core pointer. This leads to ProcessTouchOwnershipEvent() being called with reason XIRejectTouch and EmitTouchEnd() is called.

This leads us to DeliverTouchEvents() being called with an ET_TouchEnd event, which calls DeliverTouchEvent() which calls DeliverTouchEndEvent() again (listener type  LISTENER_POINTER_GRAB and state LISTENER_IS_OWNER).

After calling DeliverTouchEmulatedEvent(), which returns without triggering any of my trace points, we hit this code:

        if (ti->num_listeners > 1) {
            ev->any.type = ET_TouchUpdate;
            ev->device_event.flags |= TOUCH_PENDING_END;
            if (!(ev->device_event.flags & TOUCH_CLIENT_ID))
                ti->pending_finish = TRUE;
        }

num_listeners is 1, but device_event.flags is 0x2a (TOUCH_CLIENT_ID is set) so we don't mark it as pending_finish.

Immediately after, TouchPuntToNextOwner() is called. This replays the history, generating an ET_TouchBegin, but it doesn't generate ET_TouchEnd because pending_finish was never set.

If I modify the code snippet above to not check for TOUCH_CLIENT_ID, unconditionally setting ti->pending_finish at that point, test-xi2 reports behaviour that looks better to me. I can't say I fully understand this sequence of events and the concept of replaying touches and so on, but what does happen now is that the final generated TouchBegin is terminated with a TouchEnd, which makes sense to me given that my finger isn't on the screen.

However, this doesn't solve my original problem :( Even with this TouchEnd event added, Motion events generated by the touchpad on the virtual core pointer beyond this point have the first button bit set, even though no mouse buttons or screens are pressed. So I need to keep working on that.
Comment 4 Daniel Drake 2012-09-08 02:00:05 UTC
The mouse-button-held-down issue is fixed by http://lists.x.org/archives/xorg-devel/2012-September/033595.html
Comment 5 Peter Hutterer 2016-11-28 04:39:54 UTC
This is a mass change of bugs. Bugs assigned to me that haven't been updated in the last 3 years are closed as WONTFIX, because, well, let's at least be honest about it.

Please do not re-open unless you have a really good reason to do so (e.g. you're fixing it yourself). If it hasn't been fixed in the last 3 years, it probably won't be fixed anytime soon either. Sorry.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.