Add support for screenedge touchscreen events through XInput 2.2

Summary:
This change extends the XInputEventFilter to also listen for all touch
events on the root window.

The touch points are passed to the new gesture recognizer in screenedges.

Please note that I'm not using X11 and have hardly tested this change in
real world. To our X11 users with touch screen support: please test!

Test Plan: Can activate and deactivate the screenedge.

Reviewers: #plasma, #kwin

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D5137
This commit is contained in:
Martin Gräßlin 2017-03-22 21:03:04 +01:00
parent aa6c8f8116
commit 8672b93bdd
2 changed files with 98 additions and 10 deletions

View file

@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_GESTURES_H #ifndef KWIN_GESTURES_H
#define KWIN_GESTURES_H #define KWIN_GESTURES_H
#include <kwin_export.h>
#include <QObject> #include <QObject>
#include <QPointF> #include <QPointF>
#include <QSizeF> #include <QSizeF>
@ -179,7 +181,7 @@ private:
QSizeF m_minimumDelta; QSizeF m_minimumDelta;
}; };
class GestureRecognizer : public QObject class KWIN_EXPORT GestureRecognizer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -20,7 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "xinputintegration.h" #include "xinputintegration.h"
#include "main.h" #include "main.h"
#include "logging.h" #include "logging.h"
#include "gestures.h"
#include "platform.h" #include "platform.h"
#include "screenedge.h"
#include "x11cursor.h" #include "x11cursor.h"
#include "input.h" #include "input.h"
@ -36,16 +38,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin namespace KWin
{ {
static inline qreal fixed1616ToReal(FP1616 val)
{
return (val) * 1.0 / (1 << 16);
}
class XInputEventFilter : public X11EventFilter class XInputEventFilter : public X11EventFilter
{ {
public: public:
XInputEventFilter(int xi_opcode) XInputEventFilter(int xi_opcode)
: X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector<int>{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease}) : X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector<int>{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease, XI_TouchBegin, XI_TouchUpdate, XI_TouchOwnership, XI_TouchEnd})
{} {}
virtual ~XInputEventFilter() = default; virtual ~XInputEventFilter() = default;
bool event(xcb_generic_event_t *event) override { bool event(xcb_generic_event_t *event) override {
xcb_ge_generic_event_t *ge = reinterpret_cast<xcb_ge_generic_event_t *>(event); xcb_ge_generic_event_t *ge = reinterpret_cast<xcb_ge_generic_event_t *>(event);
xi2PrepareXIGenericDeviceEvent(ge);
switch (ge->event_type) { switch (ge->event_type) {
case XI_RawKeyPress: { case XI_RawKeyPress: {
auto re = reinterpret_cast<xXIRawEvent*>(event); auto re = reinterpret_cast<xXIRawEvent*>(event);
@ -109,6 +117,43 @@ public:
m_x11Cursor->schedulePoll(); m_x11Cursor->schedulePoll();
} }
break; break;
case XI_TouchBegin: {
auto e = reinterpret_cast<xXIDeviceEvent*>(ge);
m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)));
break;
}
case XI_TouchUpdate: {
auto e = reinterpret_cast<xXIDeviceEvent*>(event);
const QPointF touchPosition = QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y));
if (e->detail == m_trackingTouchId) {
const auto last = m_lastTouchPositions.value(e->detail);
ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(touchPosition.x() - last.x(), touchPosition.y() - last.y()));
}
m_lastTouchPositions.insert(e->detail, touchPosition);
break;
}
case XI_TouchEnd: {
auto e = reinterpret_cast<xXIDeviceEvent*>(event);
if (e->detail == m_trackingTouchId) {
ScreenEdges::self()->gestureRecognizer()->endSwipeGesture();
}
m_lastTouchPositions.remove(e->detail);
m_trackingTouchId = 0;
break;
}
case XI_TouchOwnership: {
auto e = reinterpret_cast<xXITouchOwnershipEvent*>(event);
auto it = m_lastTouchPositions.constFind(e->touchid);
if (it == m_lastTouchPositions.constEnd()) {
XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, XIRejectTouch);
} else {
if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(it.value()) > 0) {
m_trackingTouchId = e->touchid;
}
XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, m_trackingTouchId == e->touchid ? XIAcceptTouch : XIRejectTouch);
}
break;
}
default: default:
if (m_x11Cursor) { if (m_x11Cursor) {
m_x11Cursor->schedulePoll(); m_x11Cursor->schedulePoll();
@ -121,9 +166,27 @@ public:
void setCursor(const QPointer<X11Cursor> &cursor) { void setCursor(const QPointer<X11Cursor> &cursor) {
m_x11Cursor = cursor; m_x11Cursor = cursor;
} }
void setDisplay(Display *display) {
m_x11Display = display;
}
private: private:
Display *display() const {
return m_x11Display;
}
void xi2PrepareXIGenericDeviceEvent(xcb_ge_generic_event_t *event) {
// xcb event structs contain stuff that wasn't on the wire, the full_sequence field
// adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
// Move this data back to have the same layout in memory as it was on the wire
// and allow casting, overwriting the full_sequence field.
memmove((char*) event + 32, (char*) event + 36, event->length * 4);
}
QPointer<X11Cursor> m_x11Cursor; QPointer<X11Cursor> m_x11Cursor;
Display *m_x11Display = nullptr;
uint32_t m_trackingTouchId = 0;
QHash<uint32_t, QPointF> m_lastTouchPositions;
}; };
class XKeyPressReleaseEventFilter : public X11EventFilter class XKeyPressReleaseEventFilter : public X11EventFilter
@ -167,19 +230,15 @@ void XInputIntegration::init()
} }
// verify that the XInput extension is at at least version 2.0 // verify that the XInput extension is at at least version 2.0
int major = 2, minor = 0; int major = 2, minor = 2;
int result = XIQueryVersion(dpy, &major, &minor); int result = XIQueryVersion(dpy, &major, &minor);
if (result == BadImplementation) { if (result != Success) {
// Xinput 2.2 returns BadImplementation if checked against 2.0 qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput 2.2, trying 2.0";
major = 2; minor = 0;
minor = 2;
if (XIQueryVersion(dpy, &major, &minor) != Success) { if (XIQueryVersion(dpy, &major, &minor) != Success) {
qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput"; qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
return; return;
} }
} else if (result != Success) {
qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
return;
} }
m_hasXInput = true; m_hasXInput = true;
m_xiOpcode = xi_opcode; m_xiOpcode = xi_opcode;
@ -210,13 +269,40 @@ void XInputIntegration::startListening()
XISetMask(mask1, XI_RawKeyPress); XISetMask(mask1, XI_RawKeyPress);
XISetMask(mask1, XI_RawKeyRelease); XISetMask(mask1, XI_RawKeyRelease);
} }
if (m_majorVersion >=2 && m_minorVersion >= 2) {
// touch events since 2.2
XISetMask(mask1, XI_TouchBegin);
XISetMask(mask1, XI_TouchUpdate);
XISetMask(mask1, XI_TouchOwnership);
XISetMask(mask1, XI_TouchEnd);
}
evmasks[0].deviceid = XIAllMasterDevices; evmasks[0].deviceid = XIAllMasterDevices;
evmasks[0].mask_len = sizeof(mask1); evmasks[0].mask_len = sizeof(mask1);
evmasks[0].mask = mask1; evmasks[0].mask = mask1;
XISelectEvents(display(), rootWindow(), evmasks, 1); XISelectEvents(display(), rootWindow(), evmasks, 1);
if (m_majorVersion >=2 && m_minorVersion >= 2) {
XIGrabModifiers mods = { int(XIAnyModifier), 0 };
XIEventMask touchEvmasks[1];
unsigned char touchMask[XIMaskLen(XI_LASTEVENT)];
memset(touchMask, 0, sizeof(touchMask));
XISetMask(touchMask, XI_TouchBegin);
XISetMask(touchMask, XI_TouchUpdate);
XISetMask(touchMask, XI_TouchOwnership);
XISetMask(touchMask, XI_TouchEnd);
touchEvmasks[0].deviceid = XIAllMasterDevices;
touchEvmasks[0].mask_len = sizeof(touchMask);
touchEvmasks[0].mask = touchMask;
XIGrabTouchBegin(display(), XIAllMasterDevices, rootWindow(), False, touchEvmasks, 1, &mods);
}
m_xiEventFilter.reset(new XInputEventFilter(m_xiOpcode)); m_xiEventFilter.reset(new XInputEventFilter(m_xiOpcode));
m_xiEventFilter->setCursor(m_x11Cursor); m_xiEventFilter->setCursor(m_x11Cursor);
m_xiEventFilter->setDisplay(display());
m_keyPressFilter.reset(new XKeyPressReleaseEventFilter(XCB_KEY_PRESS)); m_keyPressFilter.reset(new XKeyPressReleaseEventFilter(XCB_KEY_PRESS));
m_keyReleaseFilter.reset(new XKeyPressReleaseEventFilter(XCB_KEY_RELEASE)); m_keyReleaseFilter.reset(new XKeyPressReleaseEventFilter(XCB_KEY_RELEASE));