summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/qglxintegration.cpp28
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp13
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h14
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp75
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp129
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h4
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro1
7 files changed, 213 insertions, 51 deletions
diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp
index de907f87df..30d5c911e7 100644
--- a/src/plugins/platforms/xcb/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/qglxintegration.cpp
@@ -574,12 +574,32 @@ bool QGLXContext::m_supportsThreading = true;
// binary search.
static const char *qglx_threadedgl_blacklist_renderer[] = {
"Chromium", // QTBUG-32225 (initialization fails)
- "Mesa DRI Intel(R) Sandybridge Mobile", // QTBUG-34492 (flickering in fullscreen)
0
};
+// This disables threaded rendering on anything using mesa, e.g.
+// - nvidia/nouveau
+// - amd/gallium
+// - intel
+// - some software opengl implementations
+//
+// The client glx vendor string is used to identify those setups as that seems to show the least
+// variance between the bad configurations. It's always "Mesa Project and SGI". There are some
+// configurations which don't use mesa and which can do threaded rendering (amd and nvidia chips
+// with their own proprietary drivers).
+//
+// This, of course, is very broad and disables threaded rendering on a lot of devices which would
+// be able to use it. However, the bugs listed below don't follow any easily recognizable pattern
+// and we should rather be safe.
+//
+// http://cgit.freedesktop.org/xcb/libxcb/commit/?id=be0fe56c3bcad5124dcc6c47a2fad01acd16f71a will
+// fix some of the issues. Basically, the proprietary drivers seem to have a way of working around
+// a fundamental flaw with multithreaded access to xcb, but mesa doesn't. The blacklist should be
+// reevaluated once that patch is released in some version of xcb.
static const char *qglx_threadedgl_blacklist_vendor[] = {
- "nouveau", // QTCREATORBUG-10875 (crash in creator)
+ "Mesa Project and SGI", // QTCREATORBUG-10875 (crash in creator)
+ // QTBUG-34492 (flickering in fullscreen)
+ // QTBUG-38221
0
};
@@ -627,9 +647,9 @@ void QGLXContext::queryDummyContext()
}
}
- if (const char *vendor = (const char *) glGetString(GL_VENDOR)) {
+ if (glxvendor) {
for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) {
- if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != 0) {
+ if (strstr(glxvendor, qglx_threadedgl_blacklist_vendor[i]) != 0) {
m_supportsThreading = false;
break;
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index f5f6c712c5..e3b81c2b40 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -1791,6 +1791,19 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub
return true;
}
+bool QXcbConnection::xi2GetButtonState(void *event, int buttonNum)
+{
+ xXIDeviceEvent *xideviceevent = static_cast<xXIDeviceEvent *>(event);
+ unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1];
+
+ for (int i = 0; i < (xideviceevent->buttons_len * 4); i++) {
+ if (buttonNum < 8)
+ return (buttonsMaskAddr[i] & (1 << buttonNum));
+ buttonNum -= 8;
+ }
+ return false;
+}
+
// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
// - "pad0" became "extension"
// - "pad1" and "pad" became "pad0"
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index c5fe2e33d5..1719d8ec6b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -402,6 +402,12 @@ public:
#elif defined(XCB_USE_XINPUT2)
void xi2Select(xcb_window_t window);
#endif
+#ifdef XCB_USE_XINPUT21
+ bool isUsingXInput21() { return m_xi2Enabled && m_xi2Minor >= 1; }
+#else
+ bool isUsingXInput21() { return false; }
+#endif
+
void sync();
void flush() { xcb_flush(m_connection); }
@@ -455,6 +461,10 @@ public:
QXcbSystemTrayTracker *systemTrayTracker();
+#ifdef XCB_USE_XINPUT2
+ void handleEnterEvent(const xcb_enter_notify_event_t *);
+#endif
+
private slots:
void processXcbEvents();
@@ -508,11 +518,12 @@ private:
QVector<TabletData> m_tabletData;
#endif
struct ScrollingDevice {
- ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0) { }
+ ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { }
int deviceId;
int verticalIndex, horizontalIndex;
double verticalIncrement, horizontalIncrement;
Qt::Orientations orientations;
+ Qt::Orientations legacyOrientations;
QPointF lastScrollPosition;
};
void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
@@ -522,6 +533,7 @@ private:
#if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value);
static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode);
+ static bool xi2GetButtonState(void *event, int buttonNum);
#endif
xcb_connection_t *m_connection;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index d80b49ccbb..831ccba6f6 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -134,7 +134,6 @@ void QXcbConnection::initializeXInput2()
#ifdef XCB_USE_XINPUT21
case XIScrollClass: {
XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]);
- scrollingDevice.deviceId = devices[i].deviceid;
if (sci->scroll_type == XIScrollTypeVertical) {
scrollingDevice.orientations |= Qt::Vertical;
scrollingDevice.verticalIndex = sci->number;
@@ -147,6 +146,20 @@ void QXcbConnection::initializeXInput2()
}
break;
}
+ case XIButtonClass: {
+ XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
+ for (int i=0; i < bci->num_buttons; ++i) {
+ const int buttonAtom = qatom(bci->labels[i]);
+ if (buttonAtom == QXcbAtom::ButtonWheelUp
+ || buttonAtom == QXcbAtom::ButtonWheelDown) {
+ scrollingDevice.legacyOrientations |= Qt::Vertical;
+ } else if (buttonAtom == QXcbAtom::ButtonHorizWheelLeft
+ || buttonAtom == QXcbAtom::ButtonHorizWheelRight) {
+ scrollingDevice.legacyOrientations |= Qt::Horizontal;
+ }
+ }
+ break;
+ }
#endif
default:
break;
@@ -170,7 +183,10 @@ void QXcbConnection::initializeXInput2()
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
- if (scrollingDevice.orientations) {
+ if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
+ scrollingDevice.deviceId = devices[i].deviceid;
+ // Only use legacy wheel button events when we don't have real scroll valuators.
+ scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
if (Q_UNLIKELY(debug_xinput_devices))
qDebug() << " it's a scrolling device";
@@ -256,6 +272,7 @@ void QXcbConnection::xi2Select(xcb_window_t window)
if (!m_scrollingDevices.isEmpty()) {
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
bitMask = XI_MotionMask;
+ bitMask |= XI_ButtonReleaseMask;
int i=0;
Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) {
xiEventMask[i].deviceid = scrollingDevice.deviceId;
@@ -524,6 +541,35 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
}
}
+void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *)
+{
+#ifdef XCB_USE_XINPUT21
+ QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
+ const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
+ while (it != end) {
+ ScrollingDevice& scrollingDevice = it.value();
+ int nrDevices = 0;
+ XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), scrollingDevice.deviceId, &nrDevices);
+ if (nrDevices <= 0) {
+ it = m_scrollingDevices.erase(it);
+ continue;
+ }
+ for (int c = 0; c < xiDeviceInfo->num_classes; ++c) {
+ if (xiDeviceInfo->classes[c]->type == XIValuatorClass) {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(xiDeviceInfo->classes[c]);
+ const int valuatorAtom = qatom(vci->label);
+ if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
+ scrollingDevice.lastScrollPosition.setX(vci->value);
+ else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
+ scrollingDevice.lastScrollPosition.setY(vci->value);
+ }
+ }
+ XIFreeDeviceInfo(xiDeviceInfo);
+ ++it;
+ }
+#endif
+}
+
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
#ifdef XCB_USE_XINPUT21
@@ -566,6 +612,31 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, rawDelta, angleDelta, modifiers);
}
}
+ } else if (xiEvent->evtype == XI_ButtonRelease) {
+ xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
+ QPoint angleDelta;
+ if (scrollingDevice.legacyOrientations & Qt::Vertical) {
+ if (xi2GetButtonState(xiDeviceEvent, 4))
+ angleDelta.setY(120);
+ else if (xi2GetButtonState(xiDeviceEvent, 5))
+ angleDelta.setY(-120);
+ }
+ if (scrollingDevice.legacyOrientations & Qt::Horizontal) {
+ if (xi2GetButtonState(xiDeviceEvent, 6))
+ angleDelta.setX(120);
+ if (xi2GetButtonState(xiDeviceEvent, 7))
+ angleDelta.setX(-120);
+ }
+ if (!angleDelta.isNull()) {
+ QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y));
+ QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y));
+ Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
+ if (modifiers & Qt::AltModifier)
+ std::swap(angleDelta.rx(), angleDelta.ry());
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, QPoint(), angleDelta, modifiers);
+ }
+ }
}
#else
Q_UNUSED(event);
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 5a2002f1d4..3645b6469a 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -485,7 +485,7 @@ QXcbWindow::~QXcbWindow()
void QXcbWindow::destroy()
{
if (connection()->focusWindow() == this)
- connection()->setFocusWindow(0);
+ doFocusOut();
if (m_syncCounter && m_usingSyncProtocol)
Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter));
@@ -554,7 +554,7 @@ QMargins QXcbWindow::frameMargins() const
xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, NULL);
if (reply) {
- if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1) {
+ if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1 || reply->parent == XCB_WINDOW_NONE) {
foundRoot = true;
} else {
window = parent;
@@ -671,6 +671,9 @@ void QXcbWindow::show()
Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
+ if (QGuiApplication::modalWindow() == window())
+ requestActivateWindow();
+
m_screen->windowShown(this);
connection()->sync();
@@ -694,6 +697,68 @@ void QXcbWindow::hide()
m_mapped = false;
}
+static QWindow *tlWindow(QWindow *window)
+{
+ if (window && window->parent())
+ return tlWindow(window->parent());
+ return window;
+}
+
+bool QXcbWindow::relayFocusToModalWindow() const
+{
+ QWindow *w = tlWindow(static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver());
+ QWindow *modal_window = 0;
+ if (QGuiApplicationPrivate::instance()->isWindowBlocked(w,&modal_window) && modal_window != w) {
+ modal_window->requestActivate();
+ connection()->flush();
+ return true;
+ }
+
+ return false;
+}
+
+void QXcbWindow::doFocusIn()
+{
+ if (relayFocusToModalWindow())
+ return;
+ QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
+ connection()->setFocusWindow(static_cast<QXcbWindow *>(w->handle()));
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
+}
+
+static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
+{
+ if (!event) {
+ // FocusIn event is not in the queue, proceed with FocusOut normally.
+ QWindowSystemInterface::handleWindowActivated(0, Qt::ActiveWindowFocusReason);
+ return true;
+ }
+ uint response_type = event->response_type & ~0x80;
+ if (response_type == XCB_FOCUS_IN)
+ return true;
+
+ /* We are also interested in XEMBED_FOCUS_IN events */
+ if (response_type == XCB_CLIENT_MESSAGE) {
+ xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
+ if (cme->type == connection->atom(QXcbAtom::_XEMBED)
+ && cme->data.data32[1] == XEMBED_FOCUS_IN)
+ return true;
+ }
+
+ return false;
+}
+
+void QXcbWindow::doFocusOut()
+{
+ if (relayFocusToModalWindow())
+ return;
+ connection()->setFocusWindow(0);
+ // Do not set the active window to 0 if there is a FocusIn coming.
+ // There is however no equivalent for XPutBackEvent so register a
+ // callback for QXcbConnection instead.
+ connection()->addPeekFunc(focusInPeeker);
+}
+
struct QtMotifWmHints {
quint32 flags, functions, decorations;
qint32 input_mode;
@@ -1514,6 +1579,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
QWindowSystemInterface::handleCloseEvent(window());
} else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
connection()->setTime(event->data.data32[1]);
+ relayFocusToModalWindow();
+ return;
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
if (event->window == m_screen->root())
return;
@@ -1549,8 +1616,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
} else if (event->type == atom(QXcbAtom::_XEMBED)) {
handleXEmbedMessage(event);
} else if (event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) {
- connection()->setFocusWindow(this);
- QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
+ doFocusIn();
} else if (event->type == atom(QXcbAtom::MANAGER)
|| event->type == atom(QXcbAtom::_NET_WM_STATE)
|| event->type == atom(QXcbAtom::WM_CHANGE_STATE)) {
@@ -1705,16 +1771,16 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
if (isWheel) {
-#ifndef XCB_USE_XINPUT21
- // Logic borrowed from qapplication_x11.cpp
- int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
- bool hor = (((event->detail == 4 || event->detail == 5)
- && (modifiers & Qt::AltModifier))
- || (event->detail == 6 || event->detail == 7));
-
- QWindowSystemInterface::handleWheelEvent(window(), event->time,
- local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
-#endif
+ if (!connection()->isUsingXInput21()) {
+ // Logic borrowed from qapplication_x11.cpp
+ int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
+ bool hor = (((event->detail == 4 || event->detail == 5)
+ && (modifiers & Qt::AltModifier))
+ || (event->detail == 6 || event->detail == 7));
+
+ QWindowSystemInterface::handleWheelEvent(window(), event->time,
+ local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
+ }
return;
}
@@ -1778,6 +1844,9 @@ public:
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
{
connection()->setTime(event->time);
+#ifdef XCB_USE_XINPUT2
+ connection()->handleEnterEvent(event);
+#endif
if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
|| event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
@@ -1865,41 +1934,13 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *)
{
- QWindow *w = window();
- w = static_cast<QWindowPrivate *>(QObjectPrivate::get(w))->eventReceiver();
- connection()->setFocusWindow(static_cast<QXcbWindow *>(w->handle()));
- QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
+ doFocusIn();
}
-static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
-{
- if (!event) {
- // FocusIn event is not in the queue, proceed with FocusOut normally.
- QWindowSystemInterface::handleWindowActivated(0, Qt::ActiveWindowFocusReason);
- return true;
- }
- uint response_type = event->response_type & ~0x80;
- if (response_type == XCB_FOCUS_IN)
- return true;
-
- /* We are also interested in XEMBED_FOCUS_IN events */
- if (response_type == XCB_CLIENT_MESSAGE) {
- xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
- if (cme->type == connection->atom(QXcbAtom::_XEMBED)
- && cme->data.data32[1] == XEMBED_FOCUS_IN)
- return true;
- }
-
- return false;
-}
void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *)
{
- connection()->setFocusWindow(0);
- // Do not set the active window to 0 if there is a FocusIn coming.
- // There is however no equivalent for XPutBackEvent so register a
- // callback for QXcbConnection instead.
- connection()->addPeekFunc(focusInPeeker);
+ doFocusOut();
}
void QXcbWindow::updateSyncRequestCounter()
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index a90ad7b5ed..12d17023fb 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -176,6 +176,10 @@ private:
void show();
void hide();
+ bool relayFocusToModalWindow() const;
+ void doFocusIn();
+ void doFocusOut();
+
QXcbScreen *m_screen;
xcb_window_t m_window;
diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro
index 9e4e997f55..a52aaa4a2e 100644
--- a/src/plugins/platforms/xcb/xcb-plugin.pro
+++ b/src/plugins/platforms/xcb/xcb-plugin.pro
@@ -2,6 +2,7 @@ TARGET = qxcb
PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = QXcbIntegrationPlugin
+!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
load(qt_plugin)
QT += core-private gui-private platformsupport-private