xcb: Adapt QXcbWindow::startSystemResize() for touch events

Window managers typically grab the pointer after receiving
the _NET_WM_MOVERESIZE event. But they fail to do it for
touch sequences which have a receiver. So we should reject
the touch sequence before sending _NET_WM_MOVERESIZE event.

QSizeGrip calls startSystemResize() on MouseButtonPress event
which is synthesized by Qt on TouchBegin. We can find the id
of the touch point by comparing coordinates of the synthesized
MouseButtonPress event with coordinates of all TouchBegin events.
Then we use this id to reject the touch sequence (it's possible
only after receiving XI_TouchUpdate).

Change-Id: I26519840cd221e28b0be7854e4617c9aba4b0817
Task-number: QTBUG-51385
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Alexander Volkov 2016-02-25 14:39:51 +03:00 committed by Simon Hausmann
parent d829dd3f44
commit 2488f34ecf
4 changed files with 60 additions and 3 deletions

View File

@ -504,6 +504,7 @@ public:
#endif
#ifdef XCB_USE_XINPUT22
bool startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner);
bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
#endif
Qt::MouseButton xiToQtMouseButton(uint32_t b);
@ -639,6 +640,14 @@ private:
QXcbEventReader *m_reader;
#if defined(XCB_USE_XINPUT2)
QHash<int, XInput2TouchDeviceData*> m_touchDevices;
#ifdef XCB_USE_XINPUT22
struct StartSystemResizeInfo {
xcb_window_t window;
uint16_t deviceid;
uint32_t pointid;
Qt::Corner corner;
} m_startSystemResizeInfo;
#endif
#endif
#ifdef Q_XCB_DEBUG
struct CallInfo {

View File

@ -61,9 +61,9 @@ struct XInput2TouchDeviceData {
XIDeviceInfo *xiDeviceInfo;
QTouchDevice *qtTouchDevice;
QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
// Stuff that is relevant only for touchpads
QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
QSizeF size; // device size in mm
@ -93,6 +93,7 @@ void QXcbConnection::initializeXInput2()
if (m_xi2Enabled) {
#ifdef XCB_USE_XINPUT22
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
m_startSystemResizeInfo.window = XCB_NONE;
#else
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
#endif
@ -714,7 +715,21 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
touchPoint.state = Qt::TouchPointMoved;
} else if (touchPoint.area.center() != QPoint(x, y)) {
touchPoint.state = Qt::TouchPointMoved;
dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
}
if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen &&
xiDeviceEvent->event == m_startSystemResizeInfo.window &&
xiDeviceEvent->sourceid == m_startSystemResizeInfo.deviceid &&
xiDeviceEvent->detail == m_startSystemResizeInfo.pointid) {
QXcbWindow *window = platformWindowFromId(m_startSystemResizeInfo.window);
if (window) {
XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
xiDeviceEvent->detail, xiDeviceEvent->event, XIRejectTouch);
window->doStartSystemResize(QPoint(x, y), m_startSystemResizeInfo.corner);
m_startSystemResizeInfo.window = XCB_NONE;
}
}
break;
case XI_TouchEnd:
@ -745,6 +760,27 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
touchPoint.state = Qt::TouchPointStationary;
}
bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner)
{
QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constBegin();
for (; devIt != m_touchDevices.constEnd(); ++devIt) {
XInput2TouchDeviceData *deviceData = devIt.value();
if (deviceData->qtTouchDevice->type() == QTouchDevice::TouchScreen) {
QHash<int, QPointF>::const_iterator pointIt = deviceData->pointPressedPosition.constBegin();
for (; pointIt != deviceData->pointPressedPosition.constEnd(); ++pointIt) {
if (pointIt.value().toPoint() == point) {
m_startSystemResizeInfo.window = window;
m_startSystemResizeInfo.deviceid = devIt.key();
m_startSystemResizeInfo.pointid = pointIt.key();
m_startSystemResizeInfo.corner = corner;
return true;
}
}
}
}
return false;
}
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
if (grab && !canGrab())

View File

@ -2738,13 +2738,23 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
if (!connection()->wmSupport()->isSupportedByWM(moveResize))
return false;
const QPoint globalPos = window()->mapToGlobal(pos);
#ifdef XCB_USE_XINPUT22
if (connection()->startSystemResizeForTouchBegin(m_window, globalPos, corner))
return true;
#endif
return doStartSystemResize(globalPos, corner);
}
bool QXcbWindow::doStartSystemResize(const QPoint &globalPos, Qt::Corner corner)
{
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
xcb_client_message_event_t xev;
xev.response_type = XCB_CLIENT_MESSAGE;
xev.type = moveResize;
xev.sequence = 0;
xev.window = xcb_window();
xev.format = 32;
const QPoint globalPos = window()->mapToGlobal(pos);
xev.data.data32[0] = globalPos.x();
xev.data.data32[1] = globalPos.y();
const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;

View File

@ -178,6 +178,8 @@ public:
QXcbScreen *xcbScreen() const;
bool doStartSystemResize(const QPoint &globalPos, Qt::Corner corner);
virtual void create();
virtual void destroy();