xcb: XInput2 fixes, enter/leave event fixes

Added enter/leave event handling in XInput2 to avoid problems with
those events when the mouse is grabbed.

This commit amends: 53d289ec4c
This commit amends: ed2e157803

Task-number: QTBUG-50340
Change-Id: I7a120b46daa4f8fa4c218346273ae90b6abfa156
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
This commit is contained in:
Błażej Szczygieł 2016-01-08 19:37:52 +01:00
parent f669ea0d54
commit e4fb521b3f
7 changed files with 183 additions and 95 deletions

View File

@ -613,8 +613,8 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
initializeScreens();
initializeXRender();
m_xi2Enabled = false;
#if defined(XCB_USE_XINPUT2)
m_xi2Enabled = false;
initializeXInput2();
#endif
initializeXShape();
@ -1133,8 +1133,16 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
handleClientMessageEvent((xcb_client_message_event_t *)event);
break;
case XCB_ENTER_NOTIFY:
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22() && xi2MouseEvents())
break;
#endif
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22() && xi2MouseEvents())
break;
#endif
m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state);
HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
case XCB_FOCUS_IN:
@ -2217,13 +2225,15 @@ void QXcbConnection::initializeXKB()
#endif
}
#if defined(XCB_USE_XINPUT22)
bool QXcbConnection::xi2MouseEvents() const
{
static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
// Don't use XInput2 when Xinerama extension is enabled,
// because it causes problems with multi-monitor setup.
// FIXME: Don't use XInput2 mouse events when Xinerama extension
// is enabled, because it causes problems with multi-monitor setup.
return mouseViaXI2 && !has_xinerama_extension;
}
#endif
#if defined(XCB_USE_XINPUT2)
static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)

View File

@ -348,8 +348,10 @@ public:
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
#ifdef XCB_USE_XINPUT22
virtual void handleXIMouseEvent(xcb_ge_event_t *) {}
virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
#endif
virtual QXcbWindow *toWindow() { return 0; }
};
@ -486,8 +488,8 @@ public:
static bool xEmbedSystemTrayAvailable();
static bool xEmbedSystemTrayVisualHasAlphaChannel();
#ifdef XCB_USE_XINPUT2
void handleEnterEvent(const xcb_enter_notify_event_t *);
#ifdef XCB_USE_XINPUT21
void handleEnterEvent();
#endif
#ifdef XCB_USE_XINPUT22
@ -501,7 +503,9 @@ public:
QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
#ifdef XCB_USE_XINPUT22
bool xi2MouseEvents() const;
#endif
protected:
bool event(QEvent *e) Q_DECL_OVERRIDE;
@ -535,9 +539,9 @@ private:
void initializeScreens();
bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const;
#ifdef XCB_USE_XINPUT2
bool m_xi2Enabled;
int m_xi2Minor;
#ifdef XCB_USE_XINPUT2
void initializeXInput2();
void finalizeXInput2();
void xi2SetupDevices();

View File

@ -295,6 +295,11 @@ void QXcbConnection::xi2Select(xcb_window_t window)
bitMask |= XI_ButtonPressMask;
bitMask |= XI_ButtonReleaseMask;
bitMask |= XI_MotionMask;
// There is a check for enter/leave events in plain xcb enter/leave event handler
bitMask |= XI_EnterMask;
bitMask |= XI_LeaveMask;
qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
}
XIEventMask mask;
@ -309,9 +314,12 @@ void QXcbConnection::xi2Select(xcb_window_t window)
if (result != Success)
qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
}
#endif // XCB_USE_XINPUT22
const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
#else
const bool pointerSelected = false;
#endif // XCB_USE_XINPUT22
QSet<int> tabletDevices;
#ifndef QT_NO_TABLETEVENT
if (!m_tabletData.isEmpty()) {
@ -478,6 +486,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
int sourceDeviceId = xiEvent->deviceid; // may be the master id
xXIDeviceEvent *xiDeviceEvent = 0;
xXIEnterEvent *xiEnterEvent = 0;
QXcbWindowEventListener *eventListener = 0;
switch (xiEvent->evtype) {
@ -492,14 +501,16 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
eventListener = windowEventListenerFromId(xiDeviceEvent->event);
if (eventListener) {
long result = 0;
if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
return;
}
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
case XI_Enter:
case XI_Leave: {
xiEnterEvent = reinterpret_cast<xXIEnterEvent *>(event);
eventListener = windowEventListenerFromId(xiEnterEvent->event);
sourceDeviceId = xiEnterEvent->sourceid; // use the actual device id instead of the master
break;
}
case XI_HierarchyChanged:
xi2HandleHierachyEvent(xiEvent);
return;
@ -510,13 +521,21 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
break;
}
if (eventListener) {
long result = 0;
if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
return;
}
#ifndef QT_NO_TABLETEVENT
if (!xiEnterEvent) {
for (int i = 0; i < m_tabletData.count(); ++i) {
if (m_tabletData.at(i).deviceId == sourceDeviceId) {
if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener))
return;
}
}
}
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
@ -547,6 +566,13 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xi2ProcessTouch(xiDeviceEvent, platformWindow);
break;
}
} else if (xiEnterEvent && xi2MouseEvents() && eventListener) {
switch (xiEnterEvent->evtype) {
case XI_Enter:
case XI_Leave:
eventListener->handleXIEnterLeave(event);
break;
}
}
#endif // XCB_USE_XINPUT22
}
@ -751,6 +777,8 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion);
XISetMask(mask, XI_Enter);
XISetMask(mask, XI_Leave);
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
@ -860,9 +888,9 @@ void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int
#endif
}
void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *)
{
#ifdef XCB_USE_XINPUT21
void QXcbConnection::handleEnterEvent()
{
QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
while (it != end) {
@ -878,8 +906,8 @@ void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *)
XIFreeDeviceInfo(xiDeviceInfo);
++it;
}
#endif
}
#endif
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{

View File

@ -797,9 +797,9 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
#ifdef XCB_USE_XINPUT22
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
{
#ifdef XCB_USE_XINPUT22
if (m_config && !connection()->hasXKB()) {
xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo);
xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo);
@ -815,12 +815,8 @@ void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
}
#else
Q_UNUSED(modInfo);
Q_UNUSED(groupInfo);
Q_ASSERT(false); // this can't be
#endif
}
#endif
quint32 QXcbKeyboard::xkbModMask(quint16 state)
{

View File

@ -68,7 +68,9 @@ public:
void updateXKBMods();
quint32 xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
#ifdef XCB_USE_XINPUT22
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
#endif
#ifndef QT_NO_XKB
// when XKEYBOARD is present on the X server
int coreDeviceId() const { return core_device_id; }

View File

@ -2157,6 +2157,78 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x,
handleMouseEvent(timestamp, local, global, modifiers);
}
static bool ignoreLeaveEvent(quint8 mode, quint8 detail)
{
return (mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM
|| detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL;
}
static bool ignoreEnterEvent(quint8 mode, quint8 detail)
{
return ((mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM
|| (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB)
|| detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
}
class EnterEventChecker
{
public:
bool checkEvent(xcb_generic_event_t *event)
{
if (!event)
return false;
if ((event->response_type & ~0x80) != XCB_ENTER_NOTIFY)
return false;
xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event;
if (ignoreEnterEvent(enter->mode, enter->detail))
return false;
return true;
}
};
void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
{
connection()->setTime(timestamp);
#ifdef XCB_USE_XINPUT21
connection()->handleEnterEvent();
#endif
if (ignoreEnterEvent(mode, detail))
return;
const QPoint local(event_x, event_y);
QPoint global = QPoint(root_x, root_y);
QWindowSystemInterface::handleEnterEvent(window(), local, global);
}
void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
{
connection()->setTime(timestamp);
if (ignoreLeaveEvent(mode, detail))
return;
EnterEventChecker checker;
xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)connection()->checkEvent(checker);
QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0;
if (enterWindow) {
QPoint local(enter->event_x, enter->event_y);
QPoint global = QPoint(root_x, root_y);
QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global);
} else {
QWindowSystemInterface::handleLeaveEvent(window());
}
free(enter);
}
void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
{
@ -2191,12 +2263,10 @@ static inline int fixed1616ToInt(FP1616 val)
{
return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
}
#endif
// With XI 2.2+ press/release/motion comes here instead of the above handlers.
void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event)
{
#ifdef XCB_USE_XINPUT22
QXcbConnection *conn = connection();
xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
@ -2234,12 +2304,41 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event)
qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
break;
}
#else
Q_UNUSED(event);
Q_ASSERT(false); // this can't be
#endif
}
// With XI 2.2+ enter/leave comes here and are blocked in plain xcb events
void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
{
xXIEnterEvent *ev = reinterpret_cast<xXIEnterEvent *>(event);
// Compare the window with current mouse grabber to prevent deliver events to any other windows.
// If leave event occurs and the window is under mouse - allow to deliver the leave event.
QXcbWindow *mouseGrabber = connection()->mouseGrabber();
if (mouseGrabber && mouseGrabber != this
&& (ev->evtype != XI_Leave || QGuiApplicationPrivate::currentMouseWindow != window())) {
return;
}
const int root_x = fixed1616ToInt(ev->root_x);
const int root_y = fixed1616ToInt(ev->root_y);
switch (ev->evtype) {
case XI_Enter: {
const int event_x = fixed1616ToInt(ev->event_x);
const int event_y = fixed1616ToInt(ev->event_y);
qCDebug(lcQpaXInput, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d", event_x, event_y, ev->mode, ev->detail, ev->time);
handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);
break;
}
case XI_Leave:
qCDebug(lcQpaXInput, "XI2 mouse leave, mode %d, detail %d, time %d", ev->mode, ev->detail, ev->time);
connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
handleLeaveNotifyEvent(root_x, root_y, ev->mode, ev->detail, ev->time);
break;
}
}
#endif
QXcbWindow *QXcbWindow::toWindow() { return this; }
void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers)
@ -2248,74 +2347,14 @@ void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, con
QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttons(), modifiers);
}
static bool ignoreLeaveEvent(const xcb_leave_notify_event_t *event)
{
return event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL
|| event->mode == XCB_NOTIFY_MODE_GRAB;
}
static bool ignoreEnterEvent(const xcb_enter_notify_event_t *event)
{
return (event->mode != XCB_NOTIFY_MODE_NORMAL
|| event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
}
class EnterEventChecker
{
public:
bool checkEvent(xcb_generic_event_t *event)
{
if (!event)
return false;
if ((event->response_type & ~0x80) != XCB_ENTER_NOTIFY)
return false;
xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event;
if (ignoreEnterEvent(enter))
return false;
return true;
}
};
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
{
connection()->setTime(event->time);
#ifdef XCB_USE_XINPUT2
connection()->handleEnterEvent(event);
#endif
if (ignoreEnterEvent(event))
return;
const QPoint local(event->event_x, event->event_y);
QPoint global = QPoint(event->root_x, event->root_y);
QWindowSystemInterface::handleEnterEvent(window(), local, global);
handleEnterNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->mode, event->detail, event->time);
}
void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
{
connection()->setTime(event->time);
if (ignoreLeaveEvent(event))
return;
EnterEventChecker checker;
xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)connection()->checkEvent(checker);
QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0;
if (enterWindow) {
QPoint local(enter->event_x, enter->event_y);
QPoint global = QPoint(event->root_x, event->root_y);
QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global);
} else {
QWindowSystemInterface::handleLeaveEvent(window());
}
free(enter);
handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time);
}
void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
@ -2419,7 +2458,7 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab)
if (!grab && connection()->mouseGrabber() == this)
connection()->setMouseGrabber(Q_NULLPTR);
#ifdef XCB_USE_XINPUT22
if (connection()->xi2MouseEvents()) {
if (connection()->isAtLeastXI22() && connection()->xi2MouseEvents()) {
bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab);
if (grab && result)
connection()->setMouseGrabber(this);

View File

@ -132,7 +132,10 @@ public:
void handleFocusInEvent(const xcb_focus_in_event_t *event) Q_DECL_OVERRIDE;
void handleFocusOutEvent(const xcb_focus_out_event_t *event) Q_DECL_OVERRIDE;
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE;
#ifdef XCB_USE_XINPUT22
void handleXIMouseEvent(xcb_ge_event_t *) Q_DECL_OVERRIDE;
void handleXIEnterLeave(xcb_ge_event_t *) Q_DECL_OVERRIDE;
#endif
QXcbWindow *toWindow() Q_DECL_OVERRIDE;
@ -212,6 +215,12 @@ protected:
void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
void handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp);
void handleLeaveNotifyEvent(int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp);
xcb_window_t m_window;
uint m_depth;