diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
31 files changed, 1228 insertions, 1064 deletions
diff --git a/src/plugins/platforms/xcb/README b/src/plugins/platforms/xcb/README index 2f666bebfd..15cf4cf241 100644 --- a/src/plugins/platforms/xcb/README +++ b/src/plugins/platforms/xcb/README @@ -3,14 +3,14 @@ Requires libxcb >= 1.5. PACKAGE DEPENDENCIES Required packages: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev On Ubuntu 11.10 icccm1 is replaced by icccm4 and xcb-render-util is not available: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev The packages for xcb-render-util can be installed manually from http://packages.ubuntu.com/natty/libxcb-render-util0 and http://packages.ubuntu.com/natty/libxcb-render-util0-dev On Ubuntu 12.04 icccm1 is replaced by icccm4 and xcb-render-util can be installed automatically: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev libxcb-xinerama0-dev On Fedora, the following packages are required: diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp index 10411e72e2..508f5e82e6 100644 --- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp @@ -78,6 +78,7 @@ QStringList QXcbGlIntegrationFactory::keys(const QString &pluginPath) list.append(loader()->keyMap().values()); return list; #else + Q_UNUSED(pluginPath); return QStringList(); #endif } @@ -93,6 +94,9 @@ QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform, con } if (QXcbGlIntegration *ret = loadIntegration(loader(), platform)) return ret; +#else + Q_UNUSED(platform); + Q_UNUSED(pluginPath); #endif return Q_NULLPTR; } diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 22e68e3db7..9bdedcc830 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -175,7 +175,11 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) { if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType) +#if defined(QT_OPENGL_ES_2) + m_format.setRenderableType(QSurfaceFormat::OpenGLES); +#else m_format.setRenderableType(QSurfaceFormat::OpenGL); +#endif if (m_format.renderableType() != QSurfaceFormat::OpenGL && m_format.renderableType() != QSurfaceFormat::OpenGLES) return; @@ -188,16 +192,18 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) Window window = 0; // Temporary window used to query OpenGL context if (config) { + const QByteArrayList glxExt = QByteArray(glXQueryExtensionsString(m_display, screen->screenNumber())).split(' '); + // Resolve entry point for glXCreateContextAttribsARB glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + if (glxExt.contains("GLX_ARB_create_context")) + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); - QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(m_display, screen->screenNumber())).split(' '); - bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); + const bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); // Use glXCreateContextAttribsARB if available // Also, GL ES context creation requires GLX_EXT_create_context_es2_profile - if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0 + if (glXCreateContextAttribsARB != 0 && (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) { // Try to create an OpenGL context for each known OpenGL version in descending // order from the requested version. @@ -561,10 +567,12 @@ void (*QGLXContext::getProcAddress(const QByteArray &procName)) () if (!glXGetProcAddressARB) #endif { +#ifndef QT_NO_LIBRARY extern const QString qt_gl_library_name(); // QLibrary lib(qt_gl_library_name()); QLibrary lib(QLatin1String("GL")); glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB"); +#endif } } resolved = true; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro index 57cd81ec3b..1c577e5dc9 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro @@ -17,6 +17,8 @@ contains(QT_CONFIG, xcb-glx) { LIBS += -lxcb-glx } +LIBS += $$QMAKE_LIBS_DYNLOAD + HEADERS += \ qxcbglxintegration.h \ qxcbglxwindow.h \ diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index c0f5477f82..c34bea0242 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -49,7 +49,9 @@ #include <qdebug.h> #include <qpainter.h> #include <qscreen.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qplatformgraphicsbuffer.h> +#include <private/qimage_p.h> #include <algorithm> QT_BEGIN_NAMESPACE @@ -65,6 +67,8 @@ public: QSize size() const { return m_qimage.size(); } + bool hasAlpha() const { return m_hasAlpha; } + void put(xcb_window_t window, const QPoint &dst, const QRect &source); void preparePaint(const QRegion ®ion); @@ -82,6 +86,8 @@ private: xcb_window_t m_gc_window; QRegion m_dirty; + + bool m_hasAlpha; }; class QXcbShmGraphicsBuffer : public QPlatformGraphicsBuffer @@ -145,8 +151,8 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600); if (id == -1) - qWarning("QXcbShmImage: shmget() failed (%d) for size %d (%dx%d)", - errno, segmentSize, size.width(), size.height()); + qWarning("QXcbShmImage: shmget() failed (%d: %s) for size %d (%dx%d)", + errno, strerror(errno), segmentSize, size.width(), size.height()); else m_shm_info.shmid = id; m_shm_info.shmaddr = m_xcb_image->data = (quint8 *)shmat (m_shm_info.shmid, 0, 0); @@ -171,6 +177,10 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI qWarning() << "QXcbBackingStore: Error while marking the shared memory segment to be destroyed"; } + m_hasAlpha = QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha; + if (!m_hasAlpha) + format = qt_alphaVersionForPainting(format); + m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format); m_graphics_buffer = new QXcbShmGraphicsBuffer(&m_qimage); } @@ -310,20 +320,16 @@ QPaintDevice *QXcbBackingStore::paintDevice() void QXcbBackingStore::beginPaint(const QRegion ®ion) { + if (!m_image && !m_size.isEmpty()) + resize(m_size, QRegion()); + if (!m_image) return; - - int dpr = int(m_image->image()->devicePixelRatio()); - const int windowDpr = int(window()->devicePixelRatio()); - if (windowDpr != dpr) { - resize(window()->size(), QRegion()); - dpr = int(m_image->image()->devicePixelRatio()); - } - - m_paintRegion = dpr == 1 ? region : QTransform::fromScale(dpr,dpr).map(region); + m_size = QSize(); + m_paintRegion = region; m_image->preparePaint(m_paintRegion); - if (m_image->image()->hasAlphaChannel()) { + if (m_image->hasAlpha()) { QPainter p(paintDevice()); p.setCompositionMode(QPainter::CompositionMode_Source); const QVector<QRect> rects = m_paintRegion.rects(); @@ -369,18 +375,10 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin if (!m_image || m_image->size().isEmpty()) return; - const int dpr = int(window->devicePixelRatio()); - -#ifndef QT_NO_DEBUG - const int imageDpr = int(m_image->image()->devicePixelRatio()); - if (dpr != imageDpr) - qWarning() << "QXcbBackingStore::flush() wrong devicePixelRatio for backingstore image" << dpr << imageDpr; -#endif - - QSize imageSize = m_image->size() / dpr; //because we multiply with the DPR later + QSize imageSize = m_image->size(); QRegion clipped = region; - clipped &= QRect(0, 0, window->width(), window->height()); + clipped &= QRect(QPoint(), QHighDpi::toNativePixels(window->size(), window)); clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset); QRect bounds = clipped.boundingRect(); @@ -398,8 +396,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin QVector<QRect> rects = clipped.rects(); for (int i = 0; i < rects.size(); ++i) { - QRect rect = QRect(rects.at(i).topLeft() * dpr, rects.at(i).size() * dpr); - m_image->put(platformWindow->xcb_window(), rect.topLeft(), rect.translated(offset * dpr)); + QRect rect = QRect(rects.at(i).topLeft(), rects.at(i).size()); + m_image->put(platformWindow->xcb_window(), rect.topLeft(), rect.translated(offset)); } Q_XCB_NOOP(connection()); @@ -430,13 +428,12 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c void QXcbBackingStore::resize(const QSize &size, const QRegion &) { - const int dpr = int(window()->devicePixelRatio()); - const QSize xSize = size * dpr; - if (m_image && xSize == m_image->size() && dpr == m_image->image()->devicePixelRatio()) + if (m_image && size == m_image->size()) return; Q_XCB_NOOP(connection()); - QXcbScreen *screen = static_cast<QXcbScreen *>(window()->screen()->handle()); + + QXcbScreen *screen = window()->screen() ? static_cast<QXcbScreen *>(window()->screen()->handle()) : 0; QPlatformWindow *pw = window()->handle(); if (!pw) { window()->create(); @@ -445,13 +442,16 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &) QXcbWindow* win = static_cast<QXcbWindow *>(pw); delete m_image; - m_image = new QXcbShmImage(screen, xSize, win->depth(), win->imageFormat()); - m_image->image()->setDevicePixelRatio(dpr); + if (!screen) { + m_image = 0; + m_size = size; + return; + } + m_image = new QXcbShmImage(screen, size, win->depth(), win->imageFormat()); // Slow path for bgr888 VNC: Create an additional image, paint into that and // swap R and B while copying to m_image after each paint. if (win->imageNeedsRgbSwap()) { - m_rgbImage = QImage(xSize, win->imageFormat()); - m_rgbImage.setDevicePixelRatio(dpr); + m_rgbImage = QImage(size, win->imageFormat()); } Q_XCB_NOOP(connection()); } @@ -463,14 +463,12 @@ bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy) if (!m_image || m_image->image()->isNull()) return false; - const int dpr = int(m_image->image()->devicePixelRatio()); - QRegion xArea = dpr == 1 ? area : QTransform::fromScale(dpr,dpr).map(area); m_image->preparePaint(area); - QPoint delta(dx * dpr, dy * dpr); - const QVector<QRect> xRects = xArea.rects(); - for (int i = 0; i < xRects.size(); ++i) - qt_scrollRectInImage(*m_image->image(), xRects.at(i), delta); + QPoint delta(dx, dy); + const QVector<QRect> rects = area.rects(); + for (int i = 0; i < rects.size(); ++i) + qt_scrollRectInImage(*m_image->image(), rects.at(i), delta); return true; } diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index b58a32d313..1bea36d423 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -71,6 +71,7 @@ private: QXcbShmImage *m_image; QRegion m_paintRegion; QImage m_rgbImage; + QSize m_size; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 248d1b4bbb..8b75c130fb 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -275,22 +275,8 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c) m_clientClipboard[QClipboard::Selection] = 0; m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME; m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME; + m_owner = connection()->getQtSelectionOwner(); - QXcbScreen *platformScreen = screen(); - - int x = 0, y = 0, w = 3, h = 3; - - m_owner = xcb_generate_id(xcb_connection()); - Q_XCB_CALL(xcb_create_window(xcb_connection(), - XCB_COPY_FROM_PARENT, // depth -- same as root - m_owner, // window id - platformScreen->screen()->root, // parent window id - x, y, w, h, - 0, // border width - XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class - platformScreen->screen()->root_visual, // visual - 0, // value mask - 0)); // value list #ifndef QT_NO_DEBUG QByteArray ba("Qt clipboard window"); Q_XCB_CALL(xcb_change_property(xcb_connection(), @@ -353,13 +339,7 @@ void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepte xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const { - xcb_connection_t *c = xcb_connection(); - xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom); - xcb_get_selection_owner_reply_t *reply; - reply = xcb_get_selection_owner_reply(c, cookie, 0); - xcb_window_t win = reply->owner; - free(reply); - return win; + return connection()->getSelectionOwner(atom); } xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index d2e08aecee..abb48034cd 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -60,6 +60,7 @@ #include <xcb/shm.h> #include <xcb/sync.h> #include <xcb/xfixes.h> +#include <xcb/xinerama.h> #ifdef XCB_USE_XLIB #include <X11/Xlib.h> @@ -107,6 +108,24 @@ Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") #define XCB_GE_GENERIC 35 #endif +// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: +// - "pad0" became "extension" +// - "pad1" and "pad" became "pad0" +// New and old version of this struct share the following fields: +typedef struct qt_xcb_ge_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; +} qt_xcb_ge_event_t; + +static inline bool isXIEvent(xcb_generic_event_t *event, int opCode) +{ + qt_xcb_ge_event_t *e = (qt_xcb_ge_event_t *)event; + return e->extension == opCode; +} + #ifdef XCB_USE_XLIB static const char * const xcbConnectionErrors[] = { "No error", /* Error 0 */ @@ -161,42 +180,6 @@ QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_ran return 0; } -QXcbScreen* QXcbConnection::createScreen(QXcbVirtualDesktop* virtualDesktop, - xcb_randr_output_t outputId, - xcb_randr_get_output_info_reply_t *output) -{ - QString name; - if (output) - name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output), - xcb_randr_get_output_info_name_length(output)); - else { - QByteArray displayName = m_displayName; - int dotPos = displayName.lastIndexOf('.'); - if (dotPos != -1) - displayName.truncate(dotPos); - name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + QString::number(virtualDesktop->number()); - } - - return new QXcbScreen(this, virtualDesktop, outputId, output, name); -} - -bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) -{ - xcb_generic_error_t *error = 0; - xcb_randr_get_output_primary_cookie_t primaryCookie = - xcb_randr_get_output_primary(xcb_connection(), rootWindow); - QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary ( - xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); - if (!primary || error) { - qWarning("failed to get the primary output of the screen"); - free(error); - error = NULL; - } - const bool isPrimary = primary ? (primary->output == output) : false; - - return isPrimary; -} - QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) { foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) { @@ -219,8 +202,9 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) // Not for us return; - qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc; QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc); + qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc + << "mode" << crtc.mode << "relevant screen" << screen; // Only update geometry when there's a valid mode on the CRTC // CRTC with node mode could mean that output has been disabled, and we'll // get RRNotifyOutputChange notification for that. @@ -242,17 +226,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) { qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected"; - - // Known screen removed -> delete it - m_screens.removeOne(screen); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) screen); - - QXcbIntegration::instance()->destroyScreen(screen); - - // QTBUG-40174, QTBUG-42985: If all screens are removed, wait - // and start rendering again later if a screen becomes available. - + destroyScreen(screen); } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) { // New XRandR output is available and it's enabled if (output.crtc != XCB_NONE && output.mode != XCB_NONE) { @@ -261,65 +235,142 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo( xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); - screen = createScreen(virtualDesktop, output.output, outputInfo.data()); - qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; - - screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); - foreach (QXcbScreen *otherScreen, m_screens) - if (otherScreen->root() == output.window) - otherScreen->addVirtualSibling(screen); - m_screens << screen; - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); - - // Windows which had null screens have already had expose events by now. - // They need to be told the screen is back, it's OK to render. - foreach (QWindow *window, QGuiApplication::topLevelWindows()) { - QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(screen); + // Find a fake screen + foreach (QPlatformScreen *scr, virtualDesktop->screens()) { + QXcbScreen *xcbScreen = (QXcbScreen *)scr; + if (xcbScreen->output() == XCB_NONE) { + screen = xcbScreen; + break; + } + } + + if (screen) { + QString nameWas = screen->name(); + // Transform the fake screen into a physical screen + screen->setOutput(output.output, outputInfo.data()); + updateScreen(screen, output); + qCDebug(lcQpaScreen) << "output" << screen->name() + << "is connected and enabled; was fake:" << nameWas; + } else { + screen = createScreen(virtualDesktop, output, outputInfo.data()); + qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; } } - // else ignore disabled screens } else if (screen) { - // Screen has been disabled -> remove if (output.crtc == XCB_NONE && output.mode == XCB_NONE) { + // Screen has been disabled xcb_randr_get_output_info_cookie_t outputInfoCookie = xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp); QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo( xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); if (outputInfo->crtc == XCB_NONE) { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; - m_screens.removeOne(screen); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) screen); - QXcbIntegration::instance()->destroyScreen(screen); + destroyScreen(screen); } else { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch"; + // Reset crtc to skip RRCrtcChangeNotify events, + // because they may be invalid in the middle of the mode switch + screen->setCrtc(XCB_NONE); } } else { - // Just update existing screen - screen->updateGeometry(output.config_timestamp); - const bool wasPrimary = screen->isPrimary(); - screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); - if (screen->mode() != output.mode) - screen->updateRefreshRate(output.mode); - - // If the screen became primary, reshuffle the order in QGuiApplicationPrivate - // TODO: add a proper mechanism for updating primary screen - if (!wasPrimary && screen->isPrimary()) { - QScreen *realScreen = static_cast<QPlatformScreen*>(screen)->screen(); - QGuiApplicationPrivate::screen_list.removeOne(realScreen); - QGuiApplicationPrivate::screen_list.prepend(realScreen); - m_screens.removeOne(screen); - m_screens.prepend(screen); - } + updateScreen(screen, output); qCDebug(lcQpaScreen) << "output has changed" << screen; } } + + qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + } +} + +bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) +{ + xcb_generic_error_t *error = 0; + xcb_randr_get_output_primary_cookie_t primaryCookie = + xcb_randr_get_output_primary(xcb_connection(), rootWindow); + QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary ( + xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); + if (!primary || error) { + qWarning("failed to get the primary output of the screen"); + free(error); + error = NULL; + } + const bool isPrimary = primary ? (primary->output == output) : false; + + return isPrimary; +} + +void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange) +{ + screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid + screen->updateGeometry(outputChange.config_timestamp); + if (screen->mode() != outputChange.mode) + screen->updateRefreshRate(outputChange.mode); + // Only screen which belongs to the primary virtual desktop can be a primary screen + if (screen->screenNumber() == m_primaryScreenNumber) { + if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) { + screen->setPrimary(true); + + // If the screen became primary, reshuffle the order in QGuiApplicationPrivate + const int idx = m_screens.indexOf(screen); + if (idx > 0) { + m_screens.first()->setPrimary(false); + m_screens.swap(0, idx); + } + screen->virtualDesktop()->setPrimaryScreen(screen); + QXcbIntegration::instance()->setPrimaryScreen(screen); + } + } +} + +QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, + const xcb_randr_output_change_t &outputChange, + xcb_randr_get_output_info_reply_t *outputInfo) +{ + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo); + // Only screen which belongs to the primary virtual desktop can be a primary screen + if (screen->screenNumber() == m_primaryScreenNumber) + screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output)); + + if (screen->isPrimary()) { if (!m_screens.isEmpty()) - qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); - else - qCDebug(lcQpaScreen) << "no outputs"; + m_screens.first()->setPrimary(false); + + m_screens.prepend(screen); + } else { + m_screens.append(screen); + } + virtualDesktop->addScreen(screen); + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + + return screen; +} + +void QXcbConnection::destroyScreen(QXcbScreen *screen) +{ + QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop(); + if (virtualDesktop->screens().count() == 1) { + // If there are no other screens on the same virtual desktop, + // then transform the physical screen into a fake screen. + const QString nameWas = screen->name(); + screen->setOutput(XCB_NONE, Q_NULLPTR); + qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen; + } else { + // There is more than one screen on the same virtual desktop, remove the screen + m_screens.removeOne(screen); + virtualDesktop->removeScreen(screen); + + // When primary screen is removed, set the new primary screen + // which belongs to the primary virtual desktop. + if (screen->isPrimary()) { + QXcbScreen *newPrimary = (QXcbScreen *)virtualDesktop->screens().at(0); + newPrimary->setPrimary(true); + const int idx = m_screens.indexOf(newPrimary); + if (idx > 0) + m_screens.swap(0, idx); + QXcbIntegration::instance()->setPrimaryScreen(newPrimary); + } + + QXcbIntegration::instance()->destroyScreen(screen); } } @@ -327,8 +378,7 @@ void QXcbConnection::initializeScreens() { xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int xcbScreenNumber = 0; // screen number in the xcb sense - QXcbScreen* primaryScreen = Q_NULLPTR; - bool hasOutputs = false; + QXcbScreen *primaryScreen = Q_NULLPTR; while (it.rem) { // Each "screen" in xcb terminology is a virtual desktop, // potentially a collection of separate juxtaposed monitors. @@ -338,7 +388,6 @@ void QXcbConnection::initializeScreens() QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); m_virtualDesktops.append(virtualDesktop); QList<QPlatformScreen *> siblings; - int outputCount = 0; if (has_randr_extension) { xcb_generic_error_t *error = NULL; // RRGetScreenResourcesCurrent is fast but it may return nothing if the @@ -355,7 +404,7 @@ void QXcbConnection::initializeScreens() } else { xcb_timestamp_t timestamp; xcb_randr_output_t *outputs = Q_NULLPTR; - outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data()); + int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data()); if (outputCount) { timestamp = resources_current->config_timestamp; outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data()); @@ -405,9 +454,8 @@ void QXcbConnection::initializeScreens() continue; } - QXcbScreen *screen = createScreen(virtualDesktop, outputs[i], output.data()); + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.data()); siblings << screen; - hasOutputs = true; m_screens << screen; // There can be multiple outputs per screen, use either @@ -427,46 +475,65 @@ void QXcbConnection::initializeScreens() } } } + } else if (has_xinerama_extension) { + // Xinerama is available + xcb_xinerama_query_screens_cookie_t cookie = xcb_xinerama_query_screens(m_connection); + xcb_xinerama_query_screens_reply_t *screens = xcb_xinerama_query_screens_reply(m_connection, + cookie, + Q_NULLPTR); + if (screens) { + xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens); + while (it.rem) { + xcb_xinerama_screen_info_t *screen_info = it.data; + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, + XCB_NONE, Q_NULLPTR, + screen_info, it.index); + siblings << screen; + m_screens << screen; + xcb_xinerama_screen_info_next(&it); + } + free(screens); + } } - foreach (QPlatformScreen* s, siblings) - ((QXcbScreen*)s)->setVirtualSiblings(siblings); + if (siblings.isEmpty()) { + // If there are no XRandR outputs or XRandR extension is missing, + // then create a fake/legacy screen. + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR); + qCDebug(lcQpaScreen) << "created fake screen" << screen; + m_screens << screen; + if (m_primaryScreenNumber == xcbScreenNumber) { + primaryScreen = screen; + primaryScreen->setPrimary(true); + } + siblings << screen; + } + virtualDesktop->setScreens(siblings); xcb_screen_next(&it); ++xcbScreenNumber; } // for each xcb screen - // If there's no randr extension, or there was some error above, or we found a - // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X), - // but the dimensions are known anyway, and we don't already have any lingering - // (possibly disconnected) screens, then showing windows should be possible, - // so create one screen. (QTBUG-31389) - QXcbVirtualDesktop *virtualDesktop = m_virtualDesktops.value(0); - if (virtualDesktop && !hasOutputs && !virtualDesktop->size().isEmpty() && m_screens.isEmpty()) { - QXcbScreen *screen = createScreen(virtualDesktop, 0, Q_NULLPTR); - screen->setVirtualSiblings(QList<QPlatformScreen *>() << screen); - m_screens << screen; - primaryScreen = screen; - primaryScreen->setPrimary(true); - qCDebug(lcQpaScreen) << "found a screen with zero outputs" << screen; - } + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->subscribeToXFixesSelectionNotify(); - // Ensure the primary screen is first in the list - if (primaryScreen) { - Q_ASSERT(!m_screens.isEmpty()); - if (m_screens.first() != primaryScreen) { - m_screens.removeOne(primaryScreen); - m_screens.prepend(primaryScreen); + if (m_virtualDesktops.isEmpty()) { + qFatal("QXcbConnection: no screens available"); + } else { + // Ensure the primary screen is first on the list + if (primaryScreen) { + if (m_screens.first() != primaryScreen) { + m_screens.removeOne(primaryScreen); + m_screens.prepend(primaryScreen); + } } - } - // Push the screens to QApplication - QXcbIntegration *integration = QXcbIntegration::instance(); - foreach (QXcbScreen* screen, m_screens) { - qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; - integration->screenAdded(screen, screen->isPrimary()); - } + // Push the screens to QGuiApplication + foreach (QXcbScreen *screen, m_screens) { + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + } - if (!m_screens.isEmpty()) qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + } } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) @@ -482,16 +549,19 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , xfixes_first_event(0) , xrandr_first_event(0) , xkb_first_event(0) + , has_xinerama_extension(false) , has_shape_extension(false) , has_randr_extension(false) , has_input_shape(false) , has_xkb(false) , m_buttons(0) , m_focusWindow(0) + , m_mouseGrabber(0) , m_clientLeader(0) , m_systemTrayTracker(0) , m_glIntegration(Q_NULLPTR) , m_xiGrab(false) + , m_qtSelectionOwner(0) { #ifdef XCB_USE_XLIB Display *dpy = XOpenDisplay(m_displayName.constData()); @@ -535,13 +605,13 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_time = XCB_CURRENT_TIME; m_netWmUserTime = XCB_CURRENT_TIME; - initializeXRandr(); + if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR")) + initializeXRandr(); + if (!has_randr_extension) + initializeXinerama(); + initializeXFixes(); initializeScreens(); - if (m_screens.isEmpty()) - qFatal("QXcbConnection: no screens available"); - - initializeXFixes(); initializeXRender(); m_xi2Enabled = false; #if defined(XCB_USE_XINPUT2) @@ -590,9 +660,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra qCDebug(QT_XCB_GLINTEGRATION) << "Failed to create xcb gl-integration"; sync(); - - if (qEnvironmentVariableIsEmpty("QT_IM_MODULE")) - qputenv("QT_IM_MODULE", QByteArray("compose")); } QXcbConnection::~QXcbConnection() @@ -1075,8 +1142,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_FOCUS_OUT: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); case XCB_KEY_PRESS: - m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state); + { + xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event; + m_keyboard->updateXKBStateFromCore(kp->state); + setTime(kp->time); HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); + } case XCB_KEY_RELEASE: m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state); HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); @@ -1110,12 +1181,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) handled = false; break; case XCB_PROPERTY_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + { + xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event; + if (pn->atom == atom(QXcbAtom::_NET_WORKAREA)) { + QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(pn->window); + if (virtualDesktop) + virtualDesktop->updateWorkArea(); + } else { + HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + } break; + } #if defined(XCB_USE_XINPUT2) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() - if (m_xi2Enabled) + if (m_xi2Enabled && isXIEvent(event, m_xiOpCode)) xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event)); break; #endif @@ -1127,10 +1207,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (!handled) { if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { - setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp); + xcb_xfixes_selection_notify_event_t *notify_event = (xcb_xfixes_selection_notify_event_t *)event; + setTime(notify_event->timestamp); #ifndef QT_NO_CLIPBOARD - m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event); + m_clipboard->handleXFixesSelectionRequest(notify_event); #endif + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->handleXFixesSelectionNotify(notify_event); + handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { updateScreens((xcb_randr_notify_event_t *)event); @@ -1269,6 +1353,10 @@ void QXcbConnection::setFocusWindow(QXcbWindow *w) { m_focusWindow = w; } +void QXcbConnection::setMouseGrabber(QXcbWindow *w) +{ + m_mouseGrabber = w; +} void QXcbConnection::grabServer() { @@ -1288,10 +1376,12 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id) memset(&event, 0, sizeof(event)); const xcb_window_t eventListener = xcb_generate_id(m_connection); + xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); + xcb_screen_t *screen = it.data; Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, - eventListener, m_screens.at(0)->root(), + eventListener, screen->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, - m_screens.at(0)->screen()->root_visual, 0, 0)); + screen->root_visual, 0, 0)); event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; @@ -1357,6 +1447,37 @@ xcb_timestamp_t QXcbConnection::getTimestamp() return timestamp; } +xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const +{ + xcb_connection_t *c = xcb_connection(); + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom); + xcb_get_selection_owner_reply_t *reply; + reply = xcb_get_selection_owner_reply(c, cookie, 0); + xcb_window_t win = reply->owner; + free(reply); + return win; +} + +xcb_window_t QXcbConnection::getQtSelectionOwner() +{ + if (!m_qtSelectionOwner) { + xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen(); + int x = 0, y = 0, w = 3, h = 3; + m_qtSelectionOwner = xcb_generate_id(xcb_connection()); + Q_XCB_CALL(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, // depth -- same as root + m_qtSelectionOwner, // window id + xcbScreen->root, // parent window id + x, y, w, h, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + xcbScreen->root_visual, // visual + 0, // value mask + 0)); // value list + } + return m_qtSelectionOwner; +} + xcb_window_t QXcbConnection::rootWindow() { QXcbScreen *s = primaryScreen(); @@ -1437,6 +1558,105 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const #endif +#if defined(XCB_USE_XINPUT2) +// it is safe to cast XI_* events here as long as we are only touching the first 32 bytes, +// after that position event needs memmove, see xi2PrepareXIGenericDeviceEvent +static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type) +{ + if (!isXIEvent(event, opCode)) + return false; + + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + return xiEvent->evtype == type; +} +#endif +static inline bool isValid(xcb_generic_event_t *event) +{ + return event && (event->response_type & ~0x80); +} + +/*! \internal + + Compresses events of the same type to avoid swamping the event queue. + If event compression is not desired there are several options what developers can do: + + 1) Write responsive applications. We drop events that have been buffered in the event + queue while waiting on unresponsive GUI thread. + 2) Use QAbstractNativeEventFilter to get all events from X connection. This is not optimal + because it requires working with native event types. + 3) Or add public API to Qt for disabling event compression QTBUG-44964 + +*/ +bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const +{ + uint responseType = event->response_type & ~0x80; + int nextIndex = currentIndex + 1; + + if (responseType == XCB_MOTION_NOTIFY) { + // compress XCB_MOTION_NOTIFY notify events + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (next->response_type == XCB_MOTION_NOTIFY) + return true; + } + return false; + } +#if defined(XCB_USE_XINPUT2) + // compress XI_* events + if (responseType == XCB_GE_GENERIC) { + if (!m_xi2Enabled) + return false; + + // compress XI_Motion + if (isXIType(event, m_xiOpCode, XI_Motion)) { + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (isXIType(next, m_xiOpCode, XI_Motion)) + return true; + } + return false; + } +#ifdef XCB_USE_XINPUT22 + // compress XI_TouchUpdate for the same touch point id + if (isXIType(event, m_xiOpCode, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); + uint32_t id = xiDeviceEvent->detail % INT_MAX; + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (isXIType(next, m_xiOpCode, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceNextEvent = reinterpret_cast<xXIDeviceEvent *>(next); + if (id == xiDeviceNextEvent->detail % INT_MAX) + return true; + } + } + return false; + } +#endif + return false; + } +#endif + if (responseType == XCB_CONFIGURE_NOTIFY) { + // compress multiple configure notify events for the same window + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (isValid(next) && next->response_type == XCB_CONFIGURE_NOTIFY + && ((xcb_configure_notify_event_t *)next)->event == ((xcb_configure_notify_event_t*)event)->event) + { + return true; + } + } + return false; + } + + return false; +} + void QXcbConnection::processXcbEvents() { int connection_error = xcb_connection_has_error(xcb_connection()); @@ -1447,61 +1667,39 @@ void QXcbConnection::processXcbEvents() QXcbEventArray *eventqueue = m_reader->lock(); - for(int i = 0; i < eventqueue->size(); ++i) { + for (int i = 0; i < eventqueue->size(); ++i) { xcb_generic_event_t *event = eventqueue->at(i); if (!event) continue; QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event); (*eventqueue)[i] = 0; - uint response_type = event->response_type & ~0x80; - - if (!response_type) { + if (!(event->response_type & ~0x80)) { handleXcbError((xcb_generic_error_t *)event); - } else { - if (response_type == XCB_MOTION_NOTIFY) { - // compress multiple motion notify events in a row - // to avoid swamping the event queue - xcb_generic_event_t *next = eventqueue->value(i+1, 0); - if (next && (next->response_type & ~0x80) == XCB_MOTION_NOTIFY) - continue; - } + continue; + } - if (response_type == XCB_CONFIGURE_NOTIFY) { - // compress multiple configure notify events for the same window - bool found = false; - for (int j = i; j < eventqueue->size(); ++j) { - xcb_generic_event_t *other = eventqueue->at(j); - if (other && (other->response_type & ~0x80) == XCB_CONFIGURE_NOTIFY - && ((xcb_configure_notify_event_t *)other)->event == ((xcb_configure_notify_event_t *)event)->event) - { - found = true; - break; - } - } - if (found) - continue; - } + if (compressEvent(event, i, eventqueue)) + continue; - bool accepted = false; - if (clipboard()->processIncr()) - clipboard()->incrTransactionPeeker(event, accepted); - if (accepted) - continue; + bool accepted = false; + if (clipboard()->processIncr()) + clipboard()->incrTransactionPeeker(event, accepted); + if (accepted) + continue; - QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); - while (it != m_peekFuncs.end()) { - // These callbacks return true if the event is what they were - // waiting for, remove them from the list in that case. - if ((*it)(this, event)) - it = m_peekFuncs.erase(it); - else - ++it; - } - m_reader->unlock(); - handleXcbEvent(event); - m_reader->lock(); + QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); + while (it != m_peekFuncs.end()) { + // These callbacks return true if the event is what they were + // waiting for, remove them from the list in that case. + if ((*it)(this, event)) + it = m_peekFuncs.erase(it); + else + ++it; } + m_reader->unlock(); + handleXcbEvent(event); + m_reader->lock(); } eventqueue->clear(); @@ -1746,7 +1944,8 @@ static const char * xcb_atomnames = { "_XSETTINGS_SETTINGS\0" "_COMPIZ_DECOR_PENDING\0" "_COMPIZ_DECOR_REQUEST\0" - "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. + "_COMPIZ_DECOR_DELETE_PIXMAP\0" + "_COMPIZ_TOOLKIT_ACTION\0" // \0\0 terminates loop. }; QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const @@ -1917,6 +2116,22 @@ void QXcbConnection::initializeXRandr() } } +void QXcbConnection::initializeXinerama() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xinerama_id); + if (!reply || !reply->present) + return; + + xcb_generic_error_t *error = Q_NULLPTR; + xcb_xinerama_is_active_cookie_t xinerama_query_cookie = xcb_xinerama_is_active(m_connection); + xcb_xinerama_is_active_reply_t *xinerama_is_active = xcb_xinerama_is_active_reply(m_connection, + xinerama_query_cookie, + &error); + has_xinerama_extension = xinerama_is_active && !error && xinerama_is_active->state; + free(error); + free(xinerama_is_active); +} + void QXcbConnection::initializeXShape() { const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id); @@ -2004,7 +2219,9 @@ void QXcbConnection::initializeXKB() bool QXcbConnection::xi2MouseEvents() const { static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE"); - return mouseViaXI2; + // Don't use XInput2 when Xinerama extension is enabled, + // because it causes problems with multi-monitor setup. + return mouseViaXI2 && !has_xinerama_extension; } #if defined(XCB_USE_XINPUT2) @@ -2043,41 +2260,21 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub return true; } -// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: -// - "pad0" became "extension" -// - "pad1" and "pad" became "pad0" -// New and old version of this struct share the following fields: -// NOTE: API might change again in the next release of xcb in which case this comment will -// need to be updated to reflect the reality. -typedef struct qt_xcb_ge_event_t { - uint8_t response_type; - uint8_t extension; - uint16_t sequence; - uint32_t length; - uint16_t event_type; -} qt_xcb_ge_event_t; - -bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) -{ - qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; - // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from - // the xcb version 1.9.3, prior to that it was called "pad0". - if (event->extension == opCode) { - // 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); - return true; - } - return false; +void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_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); } #endif // defined(XCB_USE_XINPUT2) -QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() +QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const { if (!m_systemTrayTracker) { - if ( (m_systemTrayTracker = QXcbSystemTrayTracker::create(this)) ) { + QXcbConnection *self = const_cast<QXcbConnection *>(this); + if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) { connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)), QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*))); } @@ -2085,6 +2282,22 @@ QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() return m_systemTrayTracker; } +bool QXcbConnection::xEmbedSystemTrayAvailable() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker(); +} + +bool QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker() && connection->systemTrayTracker()->visualHasAlphaChannel(); +} + bool QXcbConnection::event(QEvent *e) { if (e->type() == QEvent::User + 1) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index a183a72353..a6a7b9e7ca 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -286,6 +286,7 @@ namespace QXcbAtom { _COMPIZ_DECOR_PENDING, _COMPIZ_DECOR_REQUEST, _COMPIZ_DECOR_DELETE_PIXMAP, + _COMPIZ_TOOLKIT_ACTION, NPredefinedAtoms, @@ -375,8 +376,10 @@ public: QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } + const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; } const QList<QXcbScreen *> &screens() const { return m_screens; } int primaryScreenNumber() const { return m_primaryScreenNumber; } + QXcbVirtualDesktop *primaryVirtualDesktop() const { return m_virtualDesktops.value(m_primaryScreenNumber); } QXcbScreen *primaryScreen() const; inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } @@ -457,6 +460,8 @@ public: bool threadedEventHandling() const { return m_reader->isRunning(); } xcb_timestamp_t getTimestamp(); + xcb_window_t getSelectionOwner(xcb_atom_t atom) const; + xcb_window_t getQtSelectionOwner(); void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; } Qt::MouseButtons buttons() const { return m_buttons; } @@ -464,6 +469,8 @@ public: QXcbWindow *focusWindow() const { return m_focusWindow; } void setFocusWindow(QXcbWindow *); + QXcbWindow *mouseGrabber() const { return m_mouseGrabber; } + void setMouseGrabber(QXcbWindow *); QByteArray startupId() const { return m_startupId; } void setStartupId(const QByteArray &nextId) { m_startupId = nextId; } @@ -474,7 +481,9 @@ public: QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; } - QXcbSystemTrayTracker *systemTrayTracker(); + QXcbSystemTrayTracker *systemTrayTracker() const; + static bool xEmbedSystemTrayAvailable(); + static bool xEmbedSystemTrayVisualHasAlphaChannel(); #ifdef XCB_USE_XINPUT2 void handleEnterEvent(const xcb_enter_notify_event_t *); @@ -508,18 +517,22 @@ private: void initializeXFixes(); void initializeXRender(); void initializeXRandr(); + void initializeXinerama(); void initializeXShape(); void initializeXKB(); void handleClientMessageEvent(const xcb_client_message_event_t *event); - QXcbScreen* createScreen(QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId = XCB_NONE, - xcb_randr_get_output_info_reply_t *output = 0); QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc); QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output); QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow); + void updateScreens(const xcb_randr_notify_event_t *event); bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); + void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange); + QXcbScreen *createScreen(QXcbVirtualDesktop *virtualDesktop, + const xcb_randr_output_change_t &outputChange, + xcb_randr_get_output_info_reply_t *outputInfo); + void destroyScreen(QXcbScreen *screen); void initializeScreens(); - void updateScreens(const xcb_randr_notify_event_t *event); + bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const; bool m_xi2Enabled; int m_xi2Minor; @@ -572,7 +585,7 @@ private: QHash<int, ScrollingDevice> m_scrollingDevices; static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value); - static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode); + static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event); #endif xcb_connection_t *m_connection; @@ -629,6 +642,7 @@ private: uint32_t xrandr_first_event; uint32_t xkb_first_event; + bool has_xinerama_extension; bool has_shape_extension; bool has_randr_extension; bool has_input_shape; @@ -637,6 +651,7 @@ private: Qt::MouseButtons m_buttons; QXcbWindow *m_focusWindow; + QXcbWindow *m_mouseGrabber; xcb_window_t m_clientLeader; QByteArray m_startupId; @@ -644,6 +659,8 @@ private: QXcbGlIntegration *m_glIntegration; bool m_xiGrab; + xcb_window_t m_qtSelectionOwner; + friend class QXcbEventReader; }; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 5aa1933859..e055ad1424 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -216,6 +216,12 @@ void QXcbConnection::xi2SetupDevices() isTablet = true; tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); + } else if (name.contains("waltop") && name.contains("tablet")) { + // other "Genius" tablets + // WALTOP International Corp. Slim Tablet + isTablet = true; + tabletData.pointerType = QTabletEvent::Pen; + dbgType = QLatin1String("pen"); } else { isTablet = false; } @@ -268,7 +274,7 @@ void QXcbConnection::finalizeXInput2() void QXcbConnection::xi2Select(xcb_window_t window) { - if (!m_xi2Enabled) + if (!m_xi2Enabled || window == rootWindow()) return; Display *xDisplay = static_cast<Display *>(m_xlib_display); @@ -400,6 +406,9 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) #endif // XCB_USE_XINPUT22 case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo); + // Some devices (mice) report a resolution of 0; they will be excluded later, + // for now just prevent a division by zero + const int vciResolution = vci->resolution ? vci->resolution : 1; if (vci->label == atom(QXcbAtom::AbsMTPositionX)) caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition; else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) @@ -408,14 +417,16 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) caps |= QTouchDevice::Pressure; else if (vci->label == atom(QXcbAtom::RelX)) { hasRelativeCoords = true; - dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::RelY)) { hasRelativeCoords = true; - dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); + dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::AbsX)) { - dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); + caps |= QTouchDevice::Position; + dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution); } else if (vci->label == atom(QXcbAtom::AbsY)) { - dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); + caps |= QTouchDevice::Position; + dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution); } break; } @@ -459,82 +470,81 @@ static inline qreal fixed1616ToReal(FP1616 val) void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { - if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) { - xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); - int sourceDeviceId = xiEvent->deviceid; // may be the master id - xXIDeviceEvent *xiDeviceEvent = 0; - QXcbWindowEventListener *eventListener = 0; + xi2PrepareXIGenericDeviceEvent(event); + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + int sourceDeviceId = xiEvent->deviceid; // may be the master id + xXIDeviceEvent *xiDeviceEvent = 0; + QXcbWindowEventListener *eventListener = 0; - switch (xiEvent->evtype) { - case XI_ButtonPress: - case XI_ButtonRelease: - case XI_Motion: + switch (xiEvent->evtype) { + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: #ifdef XCB_USE_XINPUT22 - case XI_TouchBegin: - case XI_TouchUpdate: - case XI_TouchEnd: + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: #endif - { - 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_HierarchyChanged: - xi2HandleHierachyEvent(xiEvent); - return; - case XI_DeviceChanged: - xi2HandleDeviceChangedEvent(xiEvent); - return; - default: - break; + { + 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_HierarchyChanged: + xi2HandleHierachyEvent(xiEvent); + return; + case XI_DeviceChanged: + xi2HandleDeviceChangedEvent(xiEvent); + return; + default: + break; + } #ifndef QT_NO_TABLETEVENT - for (int i = 0; i < m_tabletData.count(); ++i) { - if (m_tabletData.at(i).deviceId == sourceDeviceId) { - if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener)) - return; - } + 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 - QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId); - if (device != m_scrollingDevices.end()) - xi2HandleScrollEvent(xiEvent, device.value()); + QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId); + if (device != m_scrollingDevices.end()) + xi2HandleScrollEvent(xiEvent, device.value()); #endif // XCB_USE_XINPUT21 #ifdef XCB_USE_XINPUT22 - if (xiDeviceEvent) { - switch (xiDeviceEvent->evtype) { - case XI_ButtonPress: - case XI_ButtonRelease: - case XI_Motion: - if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated)) - eventListener->handleXIMouseEvent(event); - break; + if (xiDeviceEvent) { + switch (xiDeviceEvent->evtype) { + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: + if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated)) + eventListener->handleXIMouseEvent(event); + break; - case XI_TouchBegin: - case XI_TouchUpdate: - case XI_TouchEnd: - if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) - qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x", - event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail, - fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), - fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event); - if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) - xi2ProcessTouch(xiDeviceEvent, platformWindow); - break; - } + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x", + event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail, + fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), + fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event); + if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) + xi2ProcessTouch(xiDeviceEvent, platformWindow); + break; } -#endif // XCB_USE_XINPUT22 } +#endif // XCB_USE_XINPUT22 } #ifdef XCB_USE_XINPUT22 @@ -562,10 +572,8 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo } QWindowSystemInterface::TouchPoint &touchPoint = dev->touchPoints[xiDeviceEvent->detail]; QXcbScreen* screen = platformWindow->xcbScreen(); - QPointF pos = screen->mapFromNative(QPointF(fixed1616ToReal(xiDeviceEvent->root_x), - fixed1616ToReal(xiDeviceEvent->root_y))); - qreal x = pos.x(); - qreal y = pos.y(); + qreal x = fixed1616ToReal(xiDeviceEvent->root_x); + qreal y = fixed1616ToReal(xiDeviceEvent->root_y); qreal nx = -1.0, ny = -1.0, d = 0.0; for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) { XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i]; @@ -876,9 +884,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin } } if (!angleDelta.isNull()) { - const int dpr = int(platformWindow->devicePixelRatio()); - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); + 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()); @@ -904,9 +911,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin angleDelta.setX(-120); } if (!angleDelta.isNull()) { - const int dpr = int(platformWindow->devicePixelRatio()); - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); + 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()); @@ -1028,9 +1034,8 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, Q tabletData->inProximity = true; tabletData->tool = toolIdToTabletDevice(tool); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletEnterProximityEvent(tabletData->tool, - tabletData->pointerType, - tabletData->serialId); + QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time, + tabletData->tool, tabletData->pointerType, tabletData->serialId); } else { tabletData->inProximity = false; tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]); @@ -1039,9 +1044,8 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, Q if (!tabletData->tool) tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletLeaveProximityEvent(tabletData->tool, - tabletData->pointerType, - tabletData->serialId); + QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time, + tabletData->tool, tabletData->pointerType, tabletData->serialId); } // TODO maybe have a hash of tabletData->deviceId to device data so we can // look up the tablet name here, and distinguish multiple tablets @@ -1119,13 +1123,14 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) } if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) - qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", - tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, + qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d time %d " + "pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", + tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, ev->time, fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y), fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y), (int)tabletData.buttons, pressure, xTilt, yTilt, rotation); - QWindowSystemInterface::handleTabletEvent(window, local, global, + QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global, tabletData.tool, tabletData.pointerType, tabletData.buttons, pressure, xTilt, yTilt, tangentialPressure, diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index bd880698e6..b321ed95dc 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -52,7 +52,7 @@ typedef char *(*PtrXcursorLibraryGetTheme)(void *); typedef int (*PtrXcursorLibrarySetTheme)(void *, const char *); typedef int (*PtrXcursorLibraryGetDefaultSize)(void *); -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) #include <X11/Xlib.h> enum { XCursorShape = CursorShape @@ -300,7 +300,7 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) const char *cursorStr = "cursor"; xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) static bool function_ptrs_not_initialized = true; if (function_ptrs_not_initialized) { QLibrary xcursorLib(QLatin1String("Xcursor"), 1); @@ -491,7 +491,7 @@ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) return cursor; } -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) bool updateCursorTheme(void *dpy, const QByteArray &theme) { if (!ptrXcursorLibraryGetTheme || !ptrXcursorLibrarySetTheme) @@ -535,7 +535,7 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) } return cursor; } -#endif //XCB_USE_XLIB +#endif //XCB_USE_XLIB / QT_NO_LIBRARY xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { @@ -544,7 +544,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) xcb_cursor_t cursor = XCB_NONE; // Try Xcursor first -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) if (cshape >= 0 && cshape <= Qt::LastCursor) { void *dpy = connection()->xlib_display(); // special case for non-standard dnd-* cursors @@ -607,45 +607,47 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) } #endif -void QXcbCursor::queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask) +void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask) { if (pos) *pos = QPoint(); - xcb_screen_iterator_t it = xcb_setup_roots_iterator(c->setup()); - while (it.rem) { - xcb_window_t root = it.data->root; - xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); - xcb_generic_error_t *err = 0; - xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); - if (!err && reply) { - if (pos) - *pos = QPoint(reply->root_x, reply->root_y); - if (rootWin) - *rootWin = root; - if (keybMask) - *keybMask = reply->mask; - free(reply); - return; + + xcb_window_t root = c->primaryVirtualDesktop()->root(); + xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); + xcb_generic_error_t *err = 0; + xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); + if (!err && reply) { + if (virtualDesktop) { + foreach (QXcbVirtualDesktop *vd, c->virtualDesktops()) { + if (vd->root() == reply->root) { + *virtualDesktop = vd; + break; + } + } } - free(err); + if (pos) + *pos = QPoint(reply->root_x, reply->root_y); + if (keybMask) + *keybMask = reply->mask; free(reply); - xcb_screen_next(&it); + return; } + free(err); + free(reply); } QPoint QXcbCursor::pos() const { QPoint p; queryPointer(connection(), 0, &p); - return m_screen->mapFromNative(p); + return p; } void QXcbCursor::setPos(const QPoint &pos) { - const QPoint xPos = m_screen->mapToNative(pos); - xcb_window_t root = 0; - queryPointer(connection(), &root, 0); - xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, xPos.x(), xPos.y()); + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + queryPointer(connection(), &virtualDesktop, 0); + xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y()); xcb_flush(xcb_connection()); } diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 7e5cdc6870..3c6dece1f2 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -75,7 +75,7 @@ public: QPoint pos() const Q_DECL_OVERRIDE; void setPos(const QPoint &pos) Q_DECL_OVERRIDE; - static void queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask = 0); + static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = 0); private: #ifndef QT_NO_CURSOR @@ -90,7 +90,7 @@ private: #ifndef QT_NO_CURSOR CursorHash m_cursorHash; #endif -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) static void cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 1d13adf851..9296a6d141 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -39,6 +39,7 @@ #include "qxcbwindow.h" #include "qxcbscreen.h" #include "qwindow.h" +#include "qxcbcursor.h" #include <private/qdnd_p.h> #include <qdebug.h> #include <qevent.h> @@ -51,6 +52,7 @@ #include <private/qshapedpixmapdndwindow_p.h> #include <private/qsimpledrag_p.h> +#include <private/qhighdpiscaling_p.h> QT_BEGIN_NAMESPACE @@ -71,12 +73,16 @@ QT_BEGIN_NAMESPACE const int xdnd_version = 5; +static inline xcb_window_t xcb_window(QPlatformWindow *w) +{ + return static_cast<QXcbWindow *>(w)->xcb_window(); +} + static inline xcb_window_t xcb_window(QWindow *w) { return static_cast<QXcbWindow *>(w->handle())->xcb_window(); } - static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) { xcb_window_t proxy = XCB_NONE; @@ -155,7 +161,7 @@ void QXcbDrag::init() source_time = XCB_CURRENT_TIME; target_time = XCB_CURRENT_TIME; - current_screen = 0; + QXcbCursor::queryPointer(connection(), ¤t_virtual_desktop, 0); drag_types.clear(); } @@ -185,7 +191,11 @@ void QXcbDrag::startDrag() xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(), atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData()); + + setUseCompositing(current_virtual_desktop->compositingActive()); QBasicDrag::startDrag(); + if (connection()->mouseGrabber() == Q_NULLPTR) + shapedPixmapWindow()->setMouseGrabEnabled(true); } void QXcbDrag::endDrag() @@ -297,50 +307,29 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md return 0; } -void QXcbDrag::move(const QMouseEvent *me) +void QXcbDrag::move(const QPoint &globalPos) { - // The mouse event is in the coordinate system of the window that started the drag. - // We do not know which window that was at this point, so we just use the device pixel ratio - // of the QGuiApplication. This will break once we support screens with different DPR. Fixing - // this properly requires some redesign of the drag and drop architecture. - static const int dpr = int(qApp->devicePixelRatio()); - QBasicDrag::move(me); - QPoint globalPos = me->globalPos(); if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) return; - const QList<QXcbScreen *> &screens = connection()->screens(); - QXcbScreen *screen = connection()->primaryScreen(); - for (int i = 0; i < screens.size(); ++i) { - if (screens.at(i)->geometry().contains(globalPos)) { - screen = screens.at(i); - break; - } - } - if (screen != current_screen) { - // ### need to recreate the shaped pixmap window? -// int screen = QCursor::x11Screen(); -// if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) { -// // recreate the pixmap on the new screen... -// delete xdnd_data.deco; -// QWidget* parent = object->source()->window()->x11Info().screen() == screen -// ? object->source()->window() : QApplication::desktop()->screen(screen); -// xdnd_data.deco = new QShapedPixmapWidget(parent); -// if (!QWidget::mouseGrabber()) { -// updatePixmap(); -// xdnd_data.deco->grabMouse(); -// } -// } -// xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot); - current_screen = screen; - } + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + QPoint cursorPos; + QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos); + QXcbScreen *screen = virtualDesktop->screenAt(cursorPos); + QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen); + if (virtualDesktop != current_virtual_desktop) { + setUseCompositing(virtualDesktop->compositingActive()); + recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos); + current_virtual_desktop = virtualDesktop; + } else { + QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos); + } -// qt_xdnd_current_screen = screen; - xcb_window_t rootwin = current_screen->root(); + xcb_window_t rootwin = current_virtual_desktop->root(); xcb_translate_coordinates_reply_t *translate = - ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x() * dpr, globalPos.y() * dpr); + ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); if (!translate) return; @@ -443,7 +432,7 @@ void QXcbDrag::move(const QMouseEvent *me) DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0]; if (w) - handleEnter(w->window(), &enter); + handleEnter(w, &enter, current_proxy_target); else if (target) xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter); waiting_for_status = false; @@ -463,7 +452,7 @@ void QXcbDrag::move(const QMouseEvent *me) move.type = atom(QXcbAtom::XdndPosition); move.data.data32[0] = connection()->clipboard()->owner(); move.data.data32[1] = 0; // flags - move.data.data32[2] = (globalPos.x() * dpr << 16) + globalPos.y() * dpr; + move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); move.data.data32[3] = connection()->time(); move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), QGuiApplication::keyboardModifiers())); DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window; @@ -471,15 +460,15 @@ void QXcbDrag::move(const QMouseEvent *me) source_time = connection()->time(); if (w) - handle_xdnd_position(w->window(), &move); + handle_xdnd_position(w, &move); else xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); } } -void QXcbDrag::drop(const QMouseEvent *event) +void QXcbDrag::drop(const QPoint &globalPos) { - QBasicDrag::drop(event); + QBasicDrag::drop(globalPos); if (!current_target) return; @@ -505,7 +494,7 @@ void QXcbDrag::drop(const QMouseEvent *event) connection()->time(), current_target, current_proxy_target, - (w ? w->window() : 0), + w, // current_embeddig_widget, currentDrag(), QTime::currentTime() @@ -518,7 +507,7 @@ void QXcbDrag::drop(const QMouseEvent *event) } if (w) { - handleDrop(w->window(), &drop); + handleDrop(w, &drop); } else { xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop); } @@ -664,7 +653,7 @@ static bool checkEmbedded(QWidget* w, const XEvent* xe) #endif -void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *event) +void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy) { Q_UNUSED(window); DEBUG() << "handleEnter" << window; @@ -676,6 +665,9 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev return; xdnd_dragsource = event->data.data32[0]; + if (!proxy) + proxy = xdndProxy(connection(), xdnd_dragsource); + current_proxy_target = proxy ? proxy : xdnd_dragsource; if (event->data.data32[1] & 1) { // get the types from XdndTypeList @@ -689,6 +681,7 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev length = xdnd_max_type; xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + xdnd_types.reserve(length); for (int i = 0; i < length; ++i) xdnd_types.append(atoms[i]); } @@ -704,17 +697,14 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev DEBUG() << " " << connection()->atomName(xdnd_types.at(i)); } -void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e) +void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e) { QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff); Q_ASSERT(w); QRect geometry = w->geometry(); - const int dpr = int(w->handle()->devicePixelRatio()); - - p /= dpr; p -= geometry.topLeft(); - if (!w || (w->type() == Qt::Desktop)) + if (!w || !w->window() || (w->window()->type() == Qt::Desktop)) return; if (e->data.data32[0] != xdnd_dragsource) { @@ -723,7 +713,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t } currentPosition = p; - currentWindow = w; + currentWindow = w->window(); // timestamp from the source if (e->data.data32[3] != XCB_NONE) { @@ -740,7 +730,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t supported_actions = Qt::DropActions(toDropAction(e->data.data32[4])); } - QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w,dropData,p,supported_actions); + QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w->window(),dropData,p,supported_actions); QRect answerRect(p + geometry.topLeft(), QSize(1,1)); answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry); @@ -776,7 +766,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t if (xdnd_dragsource == connection()->clipboard()->owner()) handle_xdnd_status(&response); else - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&response)); } @@ -796,7 +786,7 @@ namespace }; } -void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event) +void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event) { xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event); xcb_generic_event_t *nextEvent; @@ -830,12 +820,10 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event) updateCursor(Qt::IgnoreAction); } - static const int dpr = int(qApp->devicePixelRatio()); - if ((event->data.data32[1] & 2) == 0) { QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff); QSize s((event->data.data32[3] & 0xffff0000) >> 16, event->data.data32[3] & 0x0000ffff); - source_sameanswer = QRect(p / dpr, s / dpr); + source_sameanswer = QRect(p, s); } else { source_sameanswer = QRect(); } @@ -861,10 +849,10 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event) DEBUG("xdndHandleStatus end"); } -void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event) +void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event) { DEBUG("xdnd leave"); - if (!currentWindow || w != currentWindow.data()) + if (!currentWindow || w != currentWindow.data()->handle()) return; // sanity // ### @@ -879,7 +867,7 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event) DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource); } - QWindowSystemInterface::handleDrag(w,0,QPoint(),Qt::IgnoreAction); + QWindowSystemInterface::handleDrag(w->window(),0,QPoint(),Qt::IgnoreAction); xdnd_dragsource = 0; xdnd_types.clear(); @@ -909,7 +897,7 @@ void QXcbDrag::send_leave() w = 0; if (w) - handleLeave(w->window(), (const xcb_client_message_event_t *)&leave); + handleLeave(w, (const xcb_client_message_event_t *)&leave); else xcb_send_event(xcb_connection(), false,current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&leave); @@ -920,7 +908,7 @@ void QXcbDrag::send_leave() waiting_for_status = false; } -void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event) +void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event) { DEBUG("xdndHandleDrop"); if (!currentWindow) { @@ -970,7 +958,7 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event) finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE; finished.data.data32[1] = response.isAccepted(); // flags finished.data.data32[2] = toXdndAction(response.acceptedAction()); - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (char *)&finished)); xdnd_dragsource = 0; @@ -1135,7 +1123,11 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event } } - xcb_send_event(xcb_connection(), false, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); + xcb_window_t proxy_target = xdndProxy(connection(), event->requestor); + if (!proxy_target) + proxy_target = event->requestor; + + xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 95da76b732..c9d257906f 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -53,8 +53,8 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_DRAGANDDROP -class QMouseEvent; class QWindow; +class QPlatformWindow; class QXcbConnection; class QXcbWindow; class QXcbDropData; @@ -72,14 +72,14 @@ public: void startDrag() Q_DECL_OVERRIDE; void cancel() Q_DECL_OVERRIDE; - void move(const QMouseEvent *me) Q_DECL_OVERRIDE; - void drop(const QMouseEvent *me) Q_DECL_OVERRIDE; + void move(const QPoint &globalPos) Q_DECL_OVERRIDE; + void drop(const QPoint &globalPos) Q_DECL_OVERRIDE; void endDrag() Q_DECL_OVERRIDE; - void handleEnter(QWindow *window, const xcb_client_message_event_t *event); - void handlePosition(QWindow *w, const xcb_client_message_event_t *event); - void handleLeave(QWindow *w, const xcb_client_message_event_t *event); - void handleDrop(QWindow *, const xcb_client_message_event_t *event); + void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0); + void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event); + void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event); + void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event); void handleStatus(const xcb_client_message_event_t *event); void handleSelectionRequest(const xcb_selection_request_event_t *event); @@ -99,7 +99,7 @@ private: void init(); - void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event); + void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event); void handle_xdnd_status(const xcb_client_message_event_t *event); void send_leave(); @@ -133,7 +133,7 @@ private: // window to send events to (always valid if current_target) xcb_window_t current_proxy_target; - QXcbScreen *current_screen; + QXcbVirtualDesktop *current_virtual_desktop; // 10 minute timer used to discard old XdndDrop transactions enum { XdndDropTransactionTimeout = 600000 }; @@ -146,7 +146,7 @@ private: xcb_timestamp_t timestamp; xcb_window_t target; xcb_window_t proxy_target; - QWindow *targetWindow; + QPlatformWindow *targetWindow; // QWidget *embedding_widget; QPointer<QDrag> drag; QTime time; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index fc06f1a7b0..19e8b1de7d 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -252,6 +252,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const case ForeignWindows: return true; case SyncState: return true; case RasterGLSurface: return true; + case SwitchableWidgetComposition: return true; default: return QPlatformIntegration::hasCapability(cap); } } @@ -268,7 +269,10 @@ void QXcbIntegration::initialize() { // Perform everything that may potentially need the event dispatcher (timers, socket // notifiers) here instead of the constructor. - m_inputContext.reset(QPlatformInputContextFactory::create()); + QString icStr = QPlatformInputContextFactory::requested(); + if (icStr.isNull()) + icStr = QLatin1String("compose"); + m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); } void QXcbIntegration::moveToScreen(QWindow *window, int screen) diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index ea541e4556..2e088d3ca5 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1545,11 +1545,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const { - QByteArray chars; - chars.resize(1 + xkb_state_key_get_utf8(state, code, 0, 0)); - // equivalent of XLookupString - xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); - return QString::fromUtf8(chars); + QVarLengthArray<char, 32> chars(32); + const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL + chars.resize(size + 1); + xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + } + return QString::fromUtf8(chars.constData(), size); } void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *event) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 8bf9003af7..dfb0a125e2 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -47,6 +47,7 @@ #include <QtGui/qscreen.h> #include <QtPlatformHeaders/qxcbwindowfunctions.h> +#include <QtPlatformHeaders/qxcbintegrationfunctions.h> #ifndef QT_NO_DBUS #include "QtPlatformSupport/private/qdbusmenuconnection_p.h" @@ -76,7 +77,8 @@ static int resourceType(const QByteArray &key) QByteArrayLiteral("gettimestamp"), QByteArrayLiteral("x11screen"), QByteArrayLiteral("rootwindow"), QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingEnabled"), - QByteArrayLiteral("nofonthinting") + QByteArrayLiteral("nofonthinting"), + QByteArrayLiteral("atspibus") }; const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *result = std::find(names, end, key); @@ -85,8 +87,7 @@ static int resourceType(const QByteArray &key) QXcbNativeInterface::QXcbNativeInterface() : m_genericEventFilterType(QByteArrayLiteral("xcb_generic_event_t")), - m_sysTraySelectionAtom(XCB_ATOM_NONE), - m_systrayVisualId(XCB_NONE) + m_sysTraySelectionAtom(XCB_ATOM_NONE) { } @@ -117,22 +118,12 @@ bool QXcbNativeInterface::systemTrayAvailable(const QScreen *screen) const bool QXcbNativeInterface::requestSystemTrayWindowDock(const QWindow *window) { - const QPlatformWindow *platformWindow = window->handle(); - if (!platformWindow) - return false; - QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen()); - if (!trayTracker) - return false; - trayTracker->requestSystemTrayWindowDock(static_cast<const QXcbWindow *>(platformWindow)->xcb_window()); - return true; + return QXcbWindow::requestSystemTrayWindowDockStatic(window); } QRect QXcbNativeInterface::systemTrayWindowGlobalGeometry(const QWindow *window) { - if (const QPlatformWindow *platformWindow = window->handle()) - if (const QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen())) - return trayTracker->systemTrayWindowGlobalGeometry(static_cast<const QXcbWindow *>(platformWindow)->xcb_window()); - return QRect(); + return QXcbWindow::systemTrayWindowGlobalGeometryStatic(window); } xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const QXcbScreen *screen) @@ -163,54 +154,14 @@ xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const return selection_window; } -bool QXcbNativeInterface::systrayVisualHasAlphaChannel() { - const QXcbScreen *screen = static_cast<QXcbScreen *>(QGuiApplication::primaryScreen()->handle()); - - if (m_systrayVisualId == XCB_NONE) { - xcb_connection_t *xcb_conn = screen->xcb_connection(); - xcb_atom_t tray_atom = screen->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL); - - xcb_window_t systray_window = locateSystemTray(xcb_conn, screen); - if (systray_window == XCB_WINDOW_NONE) - return false; - - // Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom - xcb_get_property_cookie_t systray_atom_cookie; - xcb_get_property_reply_t *systray_atom_reply; - - systray_atom_cookie = xcb_get_property_unchecked(xcb_conn, false, systray_window, - tray_atom, XCB_ATOM_VISUALID, 0, 1); - systray_atom_reply = xcb_get_property_reply(xcb_conn, systray_atom_cookie, 0); - - if (!systray_atom_reply) - return false; - - if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) { - xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply); - m_systrayVisualId = vids[0]; - } - - free(systray_atom_reply); - } - - if (m_systrayVisualId != XCB_NONE) { - quint8 depth = screen->depthOfVisual(m_systrayVisualId); - return depth == 32; - } else { - return false; - } +bool QXcbNativeInterface::systrayVisualHasAlphaChannel() +{ + return QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel(); } -void QXcbNativeInterface::setParentRelativeBackPixmap(const QWindow *qwindow) +void QXcbNativeInterface::setParentRelativeBackPixmap(QWindow *window) { - if (const QPlatformWindow *platformWindow = qwindow->handle()) { - const QXcbWindow *qxwindow = static_cast<const QXcbWindow *>(platformWindow); - xcb_connection_t *xcb_conn = qxwindow->xcb_connection(); - - const quint32 mask = XCB_CW_BACK_PIXMAP; - const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE }; - Q_XCB_CALL(xcb_change_window_attributes(xcb_conn, qxwindow->xcb_window(), mask, values)); - } + QXcbWindow::setParentRelativeBackPixmapStatic(window); } void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString) @@ -233,6 +184,9 @@ void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resour case Display: result = display(); break; + case AtspiBus: + result = atspiBus(); + break; case Connection: result = connection(); break; @@ -294,6 +248,9 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr case NoFontHinting: result = xcbScreen->noFontHinting() ? this : 0; //qboolptr... break; + case RootWindow: + result = reinterpret_cast<void *>(xcbScreen->root()); + break; default: break; } @@ -389,9 +346,24 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio return func; //case sensitive - if (function == QXcbWindowFunctions::setWmWindowTypeIdentifier()) { - return QFunctionPointer(QXcbWindow::setWmWindowTypeStatic); - } + if (function == QXcbWindowFunctions::setWmWindowTypeIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetWmWindowType(QXcbWindow::setWmWindowTypeStatic)); + + if (function == QXcbWindowFunctions::setWmWindowIconTextIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetWmWindowIconText(QXcbWindow::setWindowIconTextStatic)); + + if (function == QXcbWindowFunctions::setParentRelativeBackPixmapIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetParentRelativeBackPixmap(QXcbWindow::setParentRelativeBackPixmapStatic)); + + if (function == QXcbWindowFunctions::requestSystemTrayWindowDockIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::RequestSystemTrayWindowDock(QXcbWindow::requestSystemTrayWindowDockStatic)); + + if (function == QXcbWindowFunctions::systemTrayWindowGlobalGeometryIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SystemTrayWindowGlobalGeometry(QXcbWindow::systemTrayWindowGlobalGeometryStatic)); + + if (function == QXcbIntegrationFunctions::xEmbedSystemTrayVisualHasAlphaChannelIdentifier()) + return QFunctionPointer(QXcbIntegrationFunctions::XEmbedSystemTrayVisualHasAlphaChannel(QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel)); + if (function == QXcbWindowFunctions::visualIdIdentifier()) { return QFunctionPointer(QXcbWindowFunctions::VisualId(QXcbWindow::visualIdStatic)); } @@ -466,6 +438,27 @@ void *QXcbNativeInterface::connection() return integration->defaultConnection()->xcb_connection(); } +void *QXcbNativeInterface::atspiBus() +{ + QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration()); + QXcbConnection *defaultConnection = integration->defaultConnection(); + if (defaultConnection) { + xcb_atom_t atspiBusAtom = defaultConnection->internAtom("AT_SPI_BUS"); + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(defaultConnection->xcb_connection(), false, + defaultConnection->rootWindow(), + atspiBusAtom, + XCB_ATOM_STRING, 0, 128)); + xcb_get_property_reply_t *reply = Q_XCB_CALL(xcb_get_property_reply(defaultConnection->xcb_connection(), cookie, 0)); + Q_ASSERT(!reply->bytes_after); + char *data = (char *)xcb_get_property_value(reply); + int length = xcb_get_property_value_length(reply); + QByteArray *busAddress = new QByteArray(data, length); + free(reply); + return busAddress; + } + return 0; +} + void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time) { if (screen) { diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index 721c6f4b1d..f88b710864 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -67,7 +67,8 @@ public: RootWindow, ScreenSubpixelType, ScreenAntialiasingEnabled, - NoFontHinting + NoFontHinting, + AtspiBus }; QXcbNativeInterface(); @@ -98,6 +99,7 @@ public: void *x11Screen(); void *rootWindow(); void *display(); + void *atspiBus(); void *connection(); static void setStartupId(const char *); static void setAppTime(QScreen *screen, xcb_timestamp_t time); @@ -105,7 +107,7 @@ public: Q_INVOKABLE void beep(); Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const; - Q_INVOKABLE void setParentRelativeBackPixmap(const QWindow *window); + Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window); Q_INVOKABLE bool systrayVisualHasAlphaChannel(); Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window); Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window); @@ -121,7 +123,6 @@ private: const QByteArray m_genericEventFilterType; xcb_atom_t m_sysTraySelectionAtom; - xcb_visualid_t m_systrayVisualId; static QXcbScreen *qPlatformScreenForWindow(QWindow *window); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 3dcd6a713a..f3d381b99e 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -44,6 +44,7 @@ #include <qpa/qwindowsysteminterface.h> #include <private/qmath_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> QT_BEGIN_NAMESPACE @@ -53,6 +54,12 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t , m_number(number) , m_xSettings(Q_NULLPTR) { + QByteArray cmAtomName("_NET_WM_CM_S"); + cmAtomName += QByteArray::number(m_number); + m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData()); + m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom); + + m_workArea = getWorkArea(); } QXcbVirtualDesktop::~QXcbVirtualDesktop() @@ -60,6 +67,27 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop() delete m_xSettings; } +QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const +{ + foreach (QXcbScreen *screen, connection()->screens()) { + if (screen->virtualDesktop() == this && screen->geometry().contains(pos)) + return screen; + } + return Q_NULLPTR; +} + +void QXcbVirtualDesktop::addScreen(QPlatformScreen *s) +{ + ((QXcbScreen *) s)->isPrimary() ? m_screens.prepend(s) : m_screens.append(s); +} + +void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s) +{ + const int idx = m_screens.indexOf(s); + Q_ASSERT(idx > -1); + m_screens.swap(0, idx); +} + QXcbXSettings *QXcbVirtualDesktop::xSettings() const { if (!m_xSettings) { @@ -69,24 +97,88 @@ QXcbXSettings *QXcbVirtualDesktop::xSettings() const return m_xSettings; } +bool QXcbVirtualDesktop::compositingActive() const +{ + if (connection()->hasXFixes()) + return m_compositingActive; + else + return connection()->getSelectionOwner(m_net_wm_cm_atom); +} + +void QXcbVirtualDesktop::handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event) +{ + if (notify_event->selection == m_net_wm_cm_atom) + m_compositingActive = notify_event->owner; +} + +void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify() +{ + if (connection()->hasXFixes()) { + const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask)); + } +} + +QRect QXcbVirtualDesktop::getWorkArea() const +{ + QRect r; + xcb_get_property_reply_t * workArea = + xcb_get_property_reply(xcb_connection(), + xcb_get_property_unchecked(xcb_connection(), false, screen()->root, + atom(QXcbAtom::_NET_WORKAREA), + XCB_ATOM_CARDINAL, 0, 1024), NULL); + if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) { + // If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops + // (don't mess with QXcbVirtualDesktop which represents an X screen). + // But QScreen doesn't know about that concept. In reality there could be a + // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop. + // But for now just assume the first 4 values give us the geometry of the + // "work area", AKA "available geometry" + uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea); + r = QRect(geom[0], geom[1], geom[2], geom[3]); + } else { + r = QRect(QPoint(), size()); + } + free(workArea); + return r; +} + +void QXcbVirtualDesktop::updateWorkArea() +{ + QRect workArea = getWorkArea(); + if (m_workArea != workArea) { + m_workArea = workArea; + foreach (QPlatformScreen *screen, m_screens) + ((QXcbScreen *)screen)->updateAvailableGeometry(); + } +} + +static inline QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi) +{ + return QSizeF(Q_MM_PER_INCH * size.width() / dpi.first, + Q_MM_PER_INCH * size.height() / dpi.second); +} + QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, - QString outputName) + const xcb_xinerama_screen_info_t *xineramaScreenInfo, int xineramaScreenIdx) : QXcbObject(connection) , m_virtualDesktop(virtualDesktop) , m_output(outputId) - , m_crtc(output ? output->crtc : 0) + , m_crtc(output ? output->crtc : XCB_NONE) , m_mode(XCB_NONE) , m_primary(false) , m_rotation(XCB_RANDR_ROTATION_ROTATE_0) - , m_outputName(outputName) + , m_outputName(getOutputName(output)) , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize()) , m_virtualSize(virtualDesktop->size()) , m_virtualSizeMillimeters(virtualDesktop->physicalSize()) , m_orientation(Qt::PrimaryOrientation) , m_refreshRate(60) , m_forcedDpi(-1) - , m_devicePixelRatio(1) + , m_pixelDensity(1) , m_hintStyle(QFontEngine::HintStyle(-1)) , m_noFontHinting(false) , m_subpixelType(QFontEngine::SubpixelAntialiasingType(-1)) @@ -103,26 +195,23 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe updateRefreshRate(crtc->mode); free(crtc); } - } else { - updateGeometry(output ? output->timestamp : 0); + } else if (xineramaScreenInfo) { + m_geometry = QRect(xineramaScreenInfo->x_org, xineramaScreenInfo->y_org, + xineramaScreenInfo->width, xineramaScreenInfo->height); + m_availableGeometry = m_geometry & m_virtualDesktop->workArea(); + m_sizeMillimeters = sizeInMillimeters(m_geometry.size(), virtualDpi()); + if (xineramaScreenIdx > -1) + m_outputName += QLatin1Char('-') + QString::number(xineramaScreenIdx); } - const int dpr = int(devicePixelRatio()); - if (m_geometry.isEmpty()) { - m_geometry = QRect(QPoint(), m_virtualSize/dpr); - m_nativeGeometry = QRect(QPoint(), m_virtualSize); - } + if (m_geometry.isEmpty()) + m_geometry = QRect(QPoint(), m_virtualSize); + if (m_availableGeometry.isEmpty()) m_availableGeometry = m_geometry; readXResources(); - // disable font hinting when we do UI scaling - static bool dpr_scaling_enabled = (qgetenv("QT_DEVICE_PIXEL_RATIO").toInt() > 1 - || qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"); - if (dpr_scaling_enabled) - m_noFontHinting = true; - QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> rootAttribs( xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL)); @@ -196,14 +285,29 @@ QXcbScreen::~QXcbScreen() delete m_cursor; } +QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo) +{ + QString name; + if (outputInfo) { + name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(outputInfo), + xcb_randr_get_output_info_name_length(outputInfo)); + } else { + QByteArray displayName = connection()->displayName(); + int dotPos = displayName.lastIndexOf('.'); + if (dotPos != -1) + displayName.truncate(dotPos); + name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + + QString::number(m_virtualDesktop->number()); + } + return name; +} QWindow *QXcbScreen::topLevelAt(const QPoint &p) const { xcb_window_t root = screen()->root; - int dpr = int(devicePixelRatio()); - int x = p.x() / dpr; - int y = p.y() / dpr; + int x = p.x(); + int y = p.y(); xcb_window_t parent = root; xcb_window_t child = root; @@ -237,43 +341,6 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const return 0; } - -QPoint QXcbScreen::mapToNative(const QPoint &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_geometry.topLeft()) * dpr + m_nativeGeometry.topLeft(); -} - -QPoint QXcbScreen::mapFromNative(const QPoint &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_nativeGeometry.topLeft()) / dpr + m_geometry.topLeft(); -} - -QPointF QXcbScreen::mapToNative(const QPointF &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_geometry.topLeft()) * dpr + m_nativeGeometry.topLeft(); -} - -QPointF QXcbScreen::mapFromNative(const QPointF &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_nativeGeometry.topLeft()) / dpr + m_geometry.topLeft(); -} - -QRect QXcbScreen::mapToNative(const QRect &rect) const -{ - const int dpr = int(devicePixelRatio()); - return QRect(mapToNative(rect.topLeft()), rect.size() * dpr); -} - -QRect QXcbScreen::mapFromNative(const QRect &rect) const -{ - const int dpr = int(devicePixelRatio()); - return QRect(mapFromNative(rect.topLeft()), rect.size() / dpr); -} - void QXcbScreen::windowShown(QXcbWindow *window) { // Freedesktop.org Startup Notification @@ -335,29 +402,22 @@ QDpi QXcbScreen::virtualDpi() const Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height()); } + QDpi QXcbScreen::logicalDpi() const { static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI"); if (overrideDpi) return QDpi(overrideDpi, overrideDpi); - int primaryDpr = int(connection()->screens().at(0)->devicePixelRatio()); - if (m_forcedDpi > 0) - return QDpi(m_forcedDpi/primaryDpr, m_forcedDpi/primaryDpr); - QDpi vDpi = virtualDpi(); - return QDpi(vDpi.first/primaryDpr, vDpi.second/primaryDpr); + if (m_forcedDpi > 0) { + return QDpi(m_forcedDpi, m_forcedDpi); + } + return virtualDpi(); } - -qreal QXcbScreen::devicePixelRatio() const +qreal QXcbScreen::pixelDensity() const { - static int override_dpr = qEnvironmentVariableIntValue("QT_DEVICE_PIXEL_RATIO"); - static bool auto_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"; - if (override_dpr > 0) - return override_dpr; - if (auto_dpr) - return m_devicePixelRatio; - return 1.0; + return m_pixelDensity; } QPlatformCursor *QXcbScreen::cursor() const @@ -365,6 +425,16 @@ QPlatformCursor *QXcbScreen::cursor() const return m_cursor; } +void QXcbScreen::setOutput(xcb_randr_output_t outputId, + xcb_randr_get_output_info_reply_t *outputInfo) +{ + m_output = outputId; + m_crtc = outputInfo ? outputInfo->crtc : XCB_NONE; + m_mode = XCB_NONE; + m_outputName = getOutputName(outputInfo); + // TODO: Send an event to the QScreen instance that the screen changed its name +} + /*! \brief handle the XCB screen change event and update properties @@ -433,19 +503,10 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan updateGeometry(change_event->timestamp); - QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation); QDpi ldpi = logicalDpi(); QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(), ldpi.first, ldpi.second); - - // Windows which had null screens have already had expose events by now. - // They need to be told the screen is back, it's OK to render. - foreach (QWindow *window, QGuiApplication::topLevelWindows()) { - QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(this); - } } void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) @@ -466,7 +527,6 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) { QRect xGeometry = geom; - QRect xAvailableGeometry = xGeometry; switch (rotation) { case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal m_orientation = Qt::LandscapeOrientation; @@ -489,41 +549,25 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) // It can be that physical size is unknown while virtual size // is known (probably back-calculated from DPI and resolution), // e.g. on VNC or with some hardware. - if (m_sizeMillimeters.isEmpty()) { - QDpi dpi = virtualDpi(); - m_sizeMillimeters = QSizeF(Q_MM_PER_INCH * xGeometry.width() / dpi.first, - Q_MM_PER_INCH * xGeometry.width() / dpi.second); - } - - xcb_get_property_reply_t * workArea = - xcb_get_property_reply(xcb_connection(), - xcb_get_property_unchecked(xcb_connection(), false, screen()->root, - atom(QXcbAtom::_NET_WORKAREA), - XCB_ATOM_CARDINAL, 0, 1024), NULL); - - if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) { - // If workArea->value_len > 4, the remaining ones seem to be for virtual desktops. - // But QScreen doesn't know about that concept. In reality there could be a - // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop. - // But for now just assume the first 4 values give us the geometry of the - // "work area", AKA "available geometry" - uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea); - QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]); - // Take the intersection of the desktop's available geometry with this screen's geometry - // to get the part of the available geometry which belongs to this screen. - xAvailableGeometry = xGeometry & virtualAvailableGeometry; - } - free(workArea); + if (m_sizeMillimeters.isEmpty()) + m_sizeMillimeters = sizeInMillimeters(xGeometry.size(), virtualDpi()); qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4); - m_devicePixelRatio = qRound(dpi/96); - const int dpr = int(devicePixelRatio()); // we may override m_devicePixelRatio - m_geometry = QRect(xGeometry.topLeft(), xGeometry.size()/dpr); - m_nativeGeometry = QRect(xGeometry.topLeft(), xGeometry.size()); - m_availableGeometry = QRect(mapFromNative(xAvailableGeometry.topLeft()), xAvailableGeometry.size()/dpr); + m_pixelDensity = qRound(dpi/96); + m_geometry = QRect(xGeometry.topLeft(), xGeometry.size()); + m_availableGeometry = xGeometry & m_virtualDesktop->workArea(); QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); } +void QXcbScreen::updateAvailableGeometry() +{ + QRect availableGeometry = m_geometry & m_virtualDesktop->workArea(); + if (m_availableGeometry != availableGeometry) { + m_availableGeometry = availableGeometry; + QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); + } +} + void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode) { if (!connection()->hasXRandr()) @@ -544,7 +588,8 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode) for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) { xcb_randr_mode_info_t *modeInfo = modesIter.data; if (modeInfo->id == mode) { - m_refreshRate = modeInfo->dot_clock / (modeInfo->htotal * modeInfo->vtotal); + const uint32_t dotCount = modeInfo->htotal * modeInfo->vtotal; + m_refreshRate = (dotCount != 0) ? modeInfo->dot_clock / dotCount : 0; m_mode = mode; break; } @@ -778,11 +823,9 @@ QDebug operator<<(QDebug debug, const QXcbScreen *screen) formatSizeF(debug, screen->physicalSize()); // TODO 5.6 if (debug.verbosity() > 2) { debug << ", screenNumber=" << screen->screenNumber(); - debug << ", virtualSize=" << screen->virtualSize().width() << "x" << screen->virtualSize().height() << " ("; + debug << ", virtualSize=" << screen->virtualSize().width() << 'x' << screen->virtualSize().height() << " ("; formatSizeF(debug, screen->virtualSize()); - debug << "), nativeGeometry="; - formatRect(debug, screen->nativeGeometry()); - debug << ", orientation=" << screen->orientation(); + debug << "), orientation=" << screen->orientation(); debug << ", depth=" << screen->depth(); debug << ", refreshRate=" << screen->refreshRate(); debug << ", root=" << hex << screen->root(); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index ccc30c0b84..f4de2b9dfd 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -39,6 +39,8 @@ #include <xcb/xcb.h> #include <xcb/randr.h> +#include <xcb/xfixes.h> +#include <xcb/xinerama.h> #include "qxcbobject.h" #include "qxcbscreen.h" @@ -64,30 +66,54 @@ public: int number() const { return m_number; } QSize size() const { return QSize(m_screen->width_in_pixels, m_screen->height_in_pixels); } QSize physicalSize() const { return QSize(m_screen->width_in_millimeters, m_screen->height_in_millimeters); } + xcb_window_t root() const { return m_screen->root; } + QXcbScreen *screenAt(const QPoint &pos) const; + + QList<QPlatformScreen *> screens() const { return m_screens; } + void setScreens(QList<QPlatformScreen *> sl) { m_screens = sl; } + void removeScreen(QPlatformScreen *s) { m_screens.removeOne(s); } + void addScreen(QPlatformScreen *s); + void setPrimaryScreen(QPlatformScreen *s); QXcbXSettings *xSettings() const; + bool compositingActive() const; + + QRect workArea() const { return m_workArea; } + void updateWorkArea(); + + void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event); + void subscribeToXFixesSelectionNotify(); + private: + QRect getWorkArea() const; + xcb_screen_t *m_screen; int m_number; + QList<QPlatformScreen *> m_screens; QXcbXSettings *m_xSettings; + xcb_atom_t m_net_wm_cm_atom; + bool m_compositingActive; + + QRect m_workArea; }; class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen { public: QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, - QString outputName); + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo, + const xcb_xinerama_screen_info_t *xineramaScreenInfo = Q_NULLPTR, int xineramaScreenIdx = -1); ~QXcbScreen(); + QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo); + QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE; QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE { return m_geometry; } - QRect nativeGeometry() const { return m_nativeGeometry; } QRect availableGeometry() const Q_DECL_OVERRIDE {return m_availableGeometry;} int depth() const Q_DECL_OVERRIDE { return screen()->root_depth; } QImage::Format format() const Q_DECL_OVERRIDE; @@ -96,14 +122,12 @@ public: QSizeF physicalVirtualSize() const { return m_virtualSizeMillimeters; } QDpi virtualDpi() const; QDpi logicalDpi() const Q_DECL_OVERRIDE; - qreal devicePixelRatio() const Q_DECL_OVERRIDE; + qreal pixelDensity() const Q_DECL_OVERRIDE; QPlatformCursor *cursor() const Q_DECL_OVERRIDE; qreal refreshRate() const Q_DECL_OVERRIDE { return m_refreshRate; } Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE { return m_orientation; } - QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; } - void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } - void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); } - void addVirtualSibling(QPlatformScreen *s) { ((QXcbScreen *) s)->isPrimary() ? m_siblings.prepend(s) : m_siblings.append(s); } + QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_virtualDesktop->screens(); } + QXcbVirtualDesktop *virtualDesktop() const { return m_virtualDesktop; } void setPrimary(bool primary) { m_primary = primary; } bool isPrimary() const { return m_primary; } @@ -116,6 +140,10 @@ public: xcb_randr_crtc_t crtc() const { return m_crtc; } xcb_randr_mode_t mode() const { return m_mode; } + void setOutput(xcb_randr_output_t outputId, + xcb_randr_get_output_info_reply_t *outputInfo); + void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; } + void windowShown(QXcbWindow *window); QString windowManagerName() const { return m_windowManagerName; } bool syncRequestSupported() const { return m_syncRequestSupported; } @@ -128,6 +156,7 @@ public: void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event); void updateGeometry(const QRect &geom, uint8_t rotation); void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME); + void updateAvailableGeometry(); void updateRefreshRate(xcb_randr_mode_t mode); void readXResources(); @@ -139,13 +168,6 @@ public: QXcbXSettings *xSettings() const; - QPoint mapToNative(const QPoint &pos) const; - QPoint mapFromNative(const QPoint &pos) const; - QPointF mapToNative(const QPointF &pos) const; - QPointF mapFromNative(const QPointF &pos) const; - QRect mapToNative(const QRect &rect) const; - QRect mapFromNative(const QRect &rect) const; - private: static bool xResource(const QByteArray &identifier, const QByteArray &expectedIdentifier, @@ -163,11 +185,9 @@ private: QSizeF m_outputSizeMillimeters; QSizeF m_sizeMillimeters; QRect m_geometry; - QRect m_nativeGeometry; QRect m_availableGeometry; QSize m_virtualSize; QSizeF m_virtualSizeMillimeters; - QList<QPlatformScreen *> m_siblings; Qt::ScreenOrientation m_orientation; QString m_windowManagerName; bool m_syncRequestSupported; @@ -176,7 +196,7 @@ private: QXcbCursor *m_cursor; int m_refreshRate; int m_forcedDpi; - int m_devicePixelRatio; + int m_pixelDensity; QFontEngine::HintStyle m_hintStyle; bool m_noFontHinting; QFontEngine::SubpixelAntialiasingType m_subpixelType; diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp index 328b72234a..c2101a71c1 100644 --- a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp +++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp @@ -134,6 +134,7 @@ static void sm_setProperty(const QString &name, const QStringList &value) SmPropValue *prop = new SmPropValue[value.count()]; int count = 0; QList<QByteArray> vl; + vl.reserve(value.size()); for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) { prop[count].length = (*it).length(); vl.append((*it).toUtf8()); diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp index a4fdd70b79..1f217e8de7 100644 --- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp @@ -63,14 +63,14 @@ QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection) const xcb_atom_t selection = connection->internAtom(netSysTray.constData()); if (!selection) return 0; - return new QXcbSystemTrayTracker(connection, trayAtom, selection, connection); + + return new QXcbSystemTrayTracker(connection, trayAtom, selection); } QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection, xcb_atom_t trayAtom, - xcb_atom_t selection, - QObject *parent) - : QObject(parent) + xcb_atom_t selection) + : QObject(connection) , m_selection(selection) , m_trayAtom(trayAtom) , m_connection(connection) @@ -125,6 +125,7 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow() // does not work for the QWindow parented on the tray. QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const { + xcb_connection_t *conn = m_connection->xcb_connection(); xcb_get_geometry_reply_t *geomReply = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0); @@ -161,9 +162,43 @@ void QXcbSystemTrayTracker::handleDestroyNotifyEvent(const xcb_destroy_notify_ev { if (event->window == m_trayWindow) { m_connection->removeWindowEventListener(m_trayWindow); - m_trayWindow = 0; + m_trayWindow = XCB_WINDOW_NONE; emitSystemTrayWindowChanged(); } } +bool QXcbSystemTrayTracker::visualHasAlphaChannel() +{ + if (m_trayWindow == XCB_WINDOW_NONE) + return false; + + xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL); + + // Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom + xcb_get_property_cookie_t systray_atom_cookie; + xcb_get_property_reply_t *systray_atom_reply; + + systray_atom_cookie = xcb_get_property_unchecked(m_connection->xcb_connection(), false, m_trayWindow, + tray_atom, XCB_ATOM_VISUALID, 0, 1); + systray_atom_reply = xcb_get_property_reply(m_connection->xcb_connection(), systray_atom_cookie, 0); + + if (!systray_atom_reply) + return false; + + xcb_visualid_t systrayVisualId = XCB_NONE; + if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) { + xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply); + systrayVisualId = vids[0]; + } + + free(systray_atom_reply); + + if (systrayVisualId != XCB_NONE) { + quint8 depth = m_connection->primaryScreen()->depthOfVisual(systrayVisualId); + return depth == 32; + } + + return false; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h index 9c20f1729a..b619afb9c4 100644 --- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h @@ -57,14 +57,14 @@ public: void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) Q_DECL_OVERRIDE; + bool visualHasAlphaChannel(); signals: void systemTrayWindowChanged(QScreen *screen); private: explicit QXcbSystemTrayTracker(QXcbConnection *connection, xcb_atom_t trayAtom, - xcb_atom_t selection, - QObject *parent = 0); + xcb_atom_t selection); static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection); void emitSystemTrayWindowChanged(); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 4fdebe1ebb..c6eb5aa66b 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -37,6 +37,7 @@ #include <QScreen> #include <QtGui/QIcon> #include <QtGui/QRegion> +#include <QtGui/private/qhighdpiscaling_p.h> #include "qxcbintegration.h" #include "qxcbconnection.h" @@ -46,6 +47,7 @@ #include "qxcbwmsupport.h" #include "qxcbimage.h" #include "qxcbnativeinterface.h" +#include "qxcbsystemtraytracker.h" #include <qpa/qplatformintegration.h> @@ -139,71 +141,9 @@ enum QX11EmbedMessageType { const quint32 XEMBED_VERSION = 0; -static inline QRect mapLocalGeometryToNative(const QRect &qtRect, int dpr) -{ - return QRect(qtRect.x() * dpr, qtRect.y() * dpr, qtRect.width() * dpr, qtRect.height() * dpr); -} - -// When mapping expose events to Qt rects: round top/left towards the origin and -// bottom/right away from the origin, making sure that we cover the whole widget - -static inline QPoint dpr_floor(const QPoint &p, int dpr) -{ - return QPoint(p.x()/dpr, p.y()/dpr); -} - -static inline QPoint dpr_ceil(const QPoint &p, int dpr) -{ - return QPoint((p.x() + dpr - 1) / dpr, (p.y() + dpr - 1) / dpr); -} - -static inline QSize dpr_ceil(const QSize &s, int dpr) -{ - return QSize((s.width() + dpr - 1) / dpr, (s.height() + dpr - 1) / dpr); -} - -static inline QRect mapExposeFromNative(const QRect &xRect, int dpr) -{ - return QRect(dpr_floor(xRect.topLeft(), dpr), dpr_ceil(xRect.bottomRight(), dpr)); -} - -static inline QRect mapLocalGeometryFromNative(const QRect &xRect, int dpr) -{ - return QRect(xRect.topLeft() / dpr, dpr_ceil(xRect.size(), dpr)); -} - QXcbScreen *QXcbWindow::parentScreen() { - return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : m_xcbScreen; -} - -QPoint QXcbWindow::mapToNative(const QPoint &pos, const QXcbScreen *screen) const -{ - if (parent()) - return pos * int(screen->devicePixelRatio()); - else - return screen->mapToNative(pos); -} -QPoint QXcbWindow::mapFromNative(const QPoint &pos, const QXcbScreen *screen) const -{ - if (parent()) - return pos / int(screen->devicePixelRatio()); - else - return screen->mapFromNative(pos); -} -QRect QXcbWindow::mapToNative(const QRect &rect, const QXcbScreen *screen) const -{ - if (parent()) - return mapLocalGeometryToNative(rect, int(screen->devicePixelRatio())); - else - return screen->mapToNative(rect); -} -QRect QXcbWindow::mapFromNative(const QRect &rect, const QXcbScreen *screen) const -{ - if (parent()) - return mapLocalGeometryFromNative(rect, int(screen->devicePixelRatio())); - else - return screen->mapFromNative(rect); + return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : xcbScreen(); } // Returns \c true if we should set WM_TRANSIENT_FOR on \a w @@ -326,7 +266,6 @@ static const char *wm_window_type_property_id = "_q_xcb_wm_window_type"; QXcbWindow::QXcbWindow(QWindow *window) : QPlatformWindow(window) , m_window(0) - , m_xcbScreen(0) , m_syncCounter(0) , m_gravity(XCB_GRAVITY_STATIC) , m_mapped(false) @@ -374,17 +313,14 @@ void QXcbWindow::create() destroy(); - m_deferredExpose = false; - m_configureNotifyPending = true; m_windowState = Qt::WindowNoState; Qt::WindowType type = window()->type(); QXcbScreen *currentScreen = xcbScreen(); - QRect rect = window()->geometry(); + QRect rect = windowGeometry(); QXcbScreen *platformScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); - m_xcbScreen = platformScreen; if (type == Qt::Desktop) { m_window = platformScreen->root(); m_depth = platformScreen->screen()->root_depth; @@ -424,17 +360,15 @@ void QXcbWindow::create() if (platformScreen != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); - const int dpr = int(devicePixelRatio()); - - QSize minimumSize = window()->minimumSize(); + const QSize minimumSize = windowMinimumSize(); if (rect.width() > 0 || rect.height() > 0) { - rect.setWidth(qBound(1, rect.width(), XCOORD_MAX/dpr)); - rect.setHeight(qBound(1, rect.height(), XCOORD_MAX/dpr)); + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); + rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); } else if (minimumSize.width() > 0 || minimumSize.height() > 0) { rect.setSize(minimumSize); } else { - rect.setWidth(defaultWindowWidth); - rect.setHeight(defaultWindowHeight); + rect.setWidth(QHighDpi::toNativePixels(int(defaultWindowWidth), platformScreen->QPlatformScreen::screen())); + rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen())); } xcb_window_t xcb_parent_id = platformScreen->root(); @@ -451,7 +385,8 @@ void QXcbWindow::create() resolveFormat(); #ifdef XCB_USE_XLIB - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { + if (window()->surfaceType() != QSurface::RasterSurface + && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { XVisualInfo *visualInfo = Q_NULLPTR; if (connection()->hasDefaultVisualId()) visualInfo = CREATE_VISUALINFO_FROM_DEFAULT_VISUALID(this); @@ -478,9 +413,7 @@ void QXcbWindow::create() m_visualId = visualInfo->visualid; - const QRect xRect = mapToNative(rect, platformScreen); - - m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, xRect.x(), xRect.y(), xRect.width(), xRect.height(), + m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(), 0, visualInfo->depth, InputOutput, visualInfo->visual, CWBackPixel|CWBorderPixel|CWColormap, &a); @@ -536,16 +469,14 @@ void QXcbWindow::create() } m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap); - const QRect xRect = mapToNative(rect, platformScreen); - Q_XCB_CALL(xcb_create_window(xcb_connection(), m_depth, m_window, // window id xcb_parent_id, // parent window id - xRect.x(), - xRect.y(), - xRect.width(), - xRect.height(), + rect.x(), + rect.y(), + rect.width(), + rect.height(), 0, // border width XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class m_visualId, // visual @@ -565,10 +496,7 @@ void QXcbWindow::create() properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS); properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING); - if (platformScreen->syncRequestSupported()) - m_usingSyncProtocol = supportsSyncProtocol(); - else - m_usingSyncProtocol = false; + m_usingSyncProtocol = platformScreen->syncRequestSupported(); if (m_usingSyncProtocol) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); @@ -666,12 +594,16 @@ QXcbWindow::~QXcbWindow() { if (window()->type() != Qt::ForeignWindow) destroy(); + else if (connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); } void QXcbWindow::destroy() { if (connection()->focusWindow() == this) doFocusOut(); + if (connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); if (m_syncCounter && m_usingSyncProtocol) Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter)); @@ -694,14 +626,6 @@ void QXcbWindow::destroy() m_pendingSyncRequest->invalidate(); } -void QXcbWindow::maybeSetScreen(QXcbScreen *screen) -{ - if (!window()->screen() && screen->geometry().contains(geometry().topLeft())) { - QWindowSystemInterface::handleWindowScreenChanged(window(), static_cast<QPlatformScreen *>(screen)->screen()); - QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size()))); - } -} - void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); @@ -712,13 +636,11 @@ void QXcbWindow::setGeometry(const QRect &rect) QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); if (!newScreen) - newScreen = currentScreen; + newScreen = xcbScreen(); - m_xcbScreen = newScreen; - const QRect xRect = mapToNative(rect, newScreen); - const QRect wmGeometry = windowToWmGeometry(xRect); + const QRect wmGeometry = windowToWmGeometry(rect); - if (newScreen != currentScreen) + if (newScreen && newScreen != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); if (qt_window_private(window())->positionAutomatic) { @@ -842,6 +764,13 @@ void QXcbWindow::setVisible(bool visible) hide(); } +static inline bool testShowWithoutActivating(const QWindow *window) +{ + // QWidget-attribute Qt::WA_ShowWithoutActivating. + const QVariant showWithoutActivating = window->property("_q_showWithoutActivating"); + return showWithoutActivating.isValid() && showWithoutActivating.toBool(); +} + void QXcbWindow::show() { if (window()->isTopLevel()) { @@ -889,7 +818,9 @@ void QXcbWindow::show() updateNetWmStateBeforeMap(); } - if (connection()->time() != XCB_TIME_CURRENT_TIME) + if (testShowWithoutActivating(window())) + updateNetWmUserTime(0); + else if (connection()->time() != XCB_TIME_CURRENT_TIME) updateNetWmUserTime(connection()->time()); if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow")) @@ -910,18 +841,19 @@ void QXcbWindow::hide() Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); // send synthetic UnmapNotify event according to icccm 4.1.4 - if (xcbScreen()) { - xcb_unmap_notify_event_t event; - event.response_type = XCB_UNMAP_NOTIFY; - event.event = xcbScreen()->root(); - event.window = m_window; - event.from_configure = false; - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(), - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); - } + xcb_unmap_notify_event_t event; + event.response_type = XCB_UNMAP_NOTIFY; + event.event = xcbScreen()->root(); + event.window = m_window; + event.from_configure = false; + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); xcb_flush(xcb_connection()); + if (connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); + m_mapped = false; } @@ -1179,6 +1111,9 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) mwmhints.flags |= MWM_HINTS_DECORATIONS; bool customize = flags & Qt::CustomizeWindowHint; + if (type == Qt::Window && !customize) + flags |= Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; + if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) { mwmhints.decorations |= MWM_DECOR_BORDER; mwmhints.decorations |= MWM_DECOR_RESIZEH; @@ -1243,8 +1178,6 @@ void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two) event.data.data32[3] = 0; event.data.data32[4] = 0; - if (!xcbScreen()) - return; Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); } @@ -1328,7 +1261,7 @@ void QXcbWindow::updateMotifWmHintsBeforeMap() mwmhints.flags &= ~MWM_HINTS_INPUT_MODE; } - if (window()->minimumSize() == window()->maximumSize()) { + if (windowMinimumSize() == windowMaximumSize()) { // fixed size, remove the resize handle (since mwm/dtwm // isn't smart enough to do it itself) mwmhints.flags |= MWM_HINTS_FUNCTIONS; @@ -1393,7 +1326,11 @@ void QXcbWindow::updateNetWmStateBeforeMap() void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) { xcb_window_t wid = m_window; - connection()->setNetWmUserTime(timestamp); + // If timestamp == 0, then it means that the window should not be + // initially activated. Don't update global user time for this + // special case. + if (timestamp != 0) + connection()->setNetWmUserTime(timestamp); const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); if (m_netWmUserTimeWindow || isSupportedByWM) { @@ -1493,8 +1430,6 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) xcb_parent_id = qXcbParent->xcb_window(); m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow; } else { - if (!xcbScreen()) - return; xcb_parent_id = xcbScreen()->root(); m_embedded = false; } @@ -1522,10 +1457,22 @@ void QXcbWindow::setWindowTitle(const QString &title) xcb_flush(xcb_connection()); } +void QXcbWindow::setWindowIconText(const QString &title) +{ + const QByteArray ba = title.toUtf8(); + Q_XCB_CALL(xcb_change_property(xcb_connection(), + XCB_PROP_MODE_REPLACE, + m_window, + atom(QXcbAtom::_NET_WM_ICON_NAME), + atom(QXcbAtom::UTF8_STRING), + 8, + ba.length(), + ba.constData())); +} + void QXcbWindow::setWindowIcon(const QIcon &icon) { QVector<quint32> icon_data; - if (!icon.isNull()) { QList<QSize> availableSizes = icon.availableSizes(); if (availableSizes.isEmpty()) { @@ -1602,8 +1549,7 @@ void QXcbWindow::propagateSizeHints() xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); - const int dpr = int(devicePixelRatio()); - const QRect xRect = windowToWmGeometry(mapToNative(geometry(), xcbScreen())); + const QRect xRect = windowToWmGeometry(geometry()); QWindow *win = window(); @@ -1613,10 +1559,10 @@ void QXcbWindow::propagateSizeHints() xcb_size_hints_set_size(&hints, true, xRect.width(), xRect.height()); xcb_size_hints_set_win_gravity(&hints, m_gravity); - QSize minimumSize = win->minimumSize() * dpr; - QSize maximumSize = win->maximumSize() * dpr; - QSize baseSize = win->baseSize() * dpr; - QSize sizeIncrement = win->sizeIncrement() * dpr; + QSize minimumSize = windowMinimumSize(); + QSize maximumSize = windowMaximumSize(); + QSize baseSize = windowBaseSize(); + QSize sizeIncrement = windowSizeIncrement(); if (minimumSize.width() > 0 || minimumSize.height() > 0) xcb_size_hints_set_min_size(&hints, @@ -1645,7 +1591,7 @@ void QXcbWindow::requestActivateWindow() return; } - if (!m_mapped) { + if (!m_mapped || !xcbScreen()) { m_deferredActivation = true; return; } @@ -1690,6 +1636,12 @@ void QXcbWindow::setWmWindowTypeStatic(QWindow *window, QXcbWindowFunctions::WmW static_cast<QXcbWindow *>(window->handle())->setWmWindowType(windowTypes, window->flags()); } +void QXcbWindow::setWindowIconTextStatic(QWindow *window, const QString &text) +{ + if (window->handle()) + static_cast<QXcbWindow *>(window->handle())->setWindowIconText(text); +} + uint QXcbWindow::visualIdStatic(QWindow *window) { if (window && window->handle()) @@ -1855,13 +1807,54 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W xcb_flush(xcb_connection()); } +void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window) +{ + if (window->handle()) + static_cast<QXcbWindow *>(window->handle())->setParentRelativeBackPixmap(); +} + +void QXcbWindow::setParentRelativeBackPixmap() +{ + const quint32 mask = XCB_CW_BACK_PIXMAP; + const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE }; + Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values)); +} + +bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window) +{ + if (window->handle()) + return static_cast<QXcbWindow *>(window->handle())->requestSystemTrayWindowDock(); + return false; +} + +bool QXcbWindow::requestSystemTrayWindowDock() const +{ + if (!connection()->systemTrayTracker()) + return false; + connection()->systemTrayTracker()->requestSystemTrayWindowDock(m_window); + return true; +} + +QRect QXcbWindow::systemTrayWindowGlobalGeometryStatic(const QWindow *window) +{ + if (window->handle()) + return static_cast<QXcbWindow *>(window->handle())->systemTrayWindowGlobalGeometry(); + return QRect(); +} + +QRect QXcbWindow::systemTrayWindowGlobalGeometry() const +{ + if (!connection()->systemTrayTracker()) + return QRect(); + return connection()->systemTrayTracker()->systemTrayWindowGlobalGeometry(m_window); +} + class ExposeCompressor { public: - ExposeCompressor(xcb_window_t window, QRegion *region, int devicePixelRatio) + ExposeCompressor(xcb_window_t window, QRegion *region) : m_window(window) , m_region(region) - , m_dpr(devicePixelRatio) , m_pending(true) { } @@ -1877,7 +1870,7 @@ public: return false; if (expose->count == 0) m_pending = false; - *m_region |= mapExposeFromNative(QRect(expose->x, expose->y, expose->width, expose->height), m_dpr); + *m_region |= QRect(expose->x, expose->y, expose->width, expose->height); return true; } @@ -1889,10 +1882,20 @@ public: private: xcb_window_t m_window; QRegion *m_region; - int m_dpr; bool m_pending; }; +bool QXcbWindow::compressExposeEvent(QRegion &exposeRegion) +{ + ExposeCompressor compressor(m_window, &exposeRegion); + xcb_generic_event_t *filter = 0; + do { + filter = connection()->checkEvent(compressor); + free(filter); + } while (filter); + return compressor.pending(); +} + bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result) { return QWindowSystemInterface::handleNativeEvent(window(), @@ -1903,24 +1906,17 @@ bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result) void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) { - const int dpr = int(devicePixelRatio()); - QRect x_rect(event->x, event->y, event->width, event->height); - QRect rect = mapExposeFromNative(x_rect, dpr); + QRect rect(event->x, event->y, event->width, event->height); if (m_exposeRegion.isEmpty()) m_exposeRegion = rect; else m_exposeRegion |= rect; - ExposeCompressor compressor(m_window, &m_exposeRegion, dpr); - xcb_generic_event_t *filter = 0; - do { - filter = connection()->checkEvent(compressor); - free(filter); - } while (filter); + bool pending = compressExposeEvent(m_exposeRegion); // if count is non-zero there are more expose events pending - if (event->count == 0 || !compressor.pending()) { + if (event->count == 0 || !pending) { QWindowSystemInterface::handleExposeEvent(window(), m_exposeRegion); m_exposeRegion = QRegion(); } @@ -1964,13 +1960,13 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even } #ifndef QT_NO_DRAGANDDROP } else if (event->type == atom(QXcbAtom::XdndEnter)) { - connection()->drag()->handleEnter(window(), event); + connection()->drag()->handleEnter(this, event); } else if (event->type == atom(QXcbAtom::XdndPosition)) { - connection()->drag()->handlePosition(window(), event); + connection()->drag()->handlePosition(this, event); } else if (event->type == atom(QXcbAtom::XdndLeave)) { - connection()->drag()->handleLeave(window(), event); + connection()->drag()->handleLeave(this, event); } else if (event->type == atom(QXcbAtom::XdndDrop)) { - connection()->drag()->handleDrop(window(), event); + connection()->drag()->handleDrop(this, event); #endif } else if (event->type == atom(QXcbAtom::_XEMBED)) { handleXEmbedMessage(event); @@ -1983,38 +1979,19 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even // and other messages. } else if (event->type == atom(QXcbAtom::_COMPIZ_DECOR_PENDING) || event->type == atom(QXcbAtom::_COMPIZ_DECOR_REQUEST) - || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP)) { + || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP) + || event->type == atom(QXcbAtom::_COMPIZ_TOOLKIT_ACTION)) { //silence the _COMPIZ messages for now } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } } -// Temporary workaround for bug in QPlatformScreen::screenForGeometry -// we need the native geometries to detect our screen, but that's not -// available in cross-platform code. Will be fixed properly when highDPI -// support is refactored to expose the native coordinate system. - -QXcbScreen *QXcbWindow::screenForNativeGeometry(const QRect &newGeometry) const -{ - QXcbScreen *currentScreen = xcbScreen(); - if (!currentScreen && QGuiApplication::primaryScreen()) - currentScreen = static_cast<QXcbScreen*>(QGuiApplication::primaryScreen()->handle()); - if (currentScreen && !parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) { - Q_FOREACH (QPlatformScreen* screen, currentScreen->virtualSiblings()) { - QXcbScreen *xcbScreen = static_cast<QXcbScreen*>(screen); - if (xcbScreen->nativeGeometry().intersects(newGeometry)) - return xcbScreen; - } - } - return currentScreen; -} - void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event) { bool fromSendEvent = (event->response_type & 0x80); QPoint pos(event->x, event->y); - if (!parent() && !fromSendEvent && xcbScreen()) { + if (!parent() && !fromSendEvent) { // Do not trust the position, query it instead. xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(), 0, 0); @@ -2026,26 +2003,25 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } } - const QRect nativeRect = QRect(pos, QSize(event->width, event->height)); - QXcbScreen *newScreen = parent() ? parentScreen() : screenForNativeGeometry(nativeRect); - - QXcbScreen *currentScreen = m_xcbScreen; - m_xcbScreen = newScreen; + const QRect actualGeometry = QRect(pos, QSize(event->width, event->height)); + QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(actualGeometry); if (!newScreen) return; - const QRect rect = mapFromNative(nativeRect, newScreen); - QPlatformWindow::setGeometry(rect); - QWindowSystemInterface::handleGeometryChange(window(), rect); - if (newScreen != currentScreen) - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + // Persist the actual geometry so that QWindow::geometry() can + // be queried in the resize event. + QPlatformWindow::setGeometry(actualGeometry); - m_configureNotifyPending = false; + // FIXME: In the case of the requestedGeometry not matching the actualGeometry due + // to e.g. the window manager applying restrictions to the geometry, the application + // will never see a move/resize event if the actualGeometry is the same as the current + // geometry, and may think the requested geometry was fulfilled. + QWindowSystemInterface::handleGeometryChange(window(), actualGeometry); - if (m_deferredExpose) { - m_deferredExpose = false; - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); - } + // QPlatformScreen::screen() is updated asynchronously, so we can't compare it + // with the newScreen. Just send the WindowScreenChanged event and QGuiApplication + // will make the comparison later. + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); if (m_usingSyncProtocol && m_syncState == SyncReceived) m_syncState = SyncAndConfigureReceived; @@ -2071,11 +2047,10 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const if (!m_embedded) return pos; - const int dpr = int(devicePixelRatio()); QPoint ret; xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(), - pos.x() * dpr, pos.y() * dpr); + pos.x(), pos.y()); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { @@ -2084,7 +2059,7 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const free(reply); } - return mapFromNative(ret, xcbScreen()); + return ret; } QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const @@ -2092,17 +2067,15 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const if (!m_embedded) return pos; - const int dpr = int(devicePixelRatio()); QPoint ret; - QPoint xPos = mapToNative(pos, xcbScreen()); xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcbScreen()->root(), xcb_window(), - xPos.x(), xPos.y()); + pos.x(), pos.y()); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { - ret.setX(reply->dst_x / dpr); - ret.setY(reply->dst_y / dpr); + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); free(reply); } @@ -2115,10 +2088,10 @@ void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) m_mapped = true; if (m_deferredActivation) requestActivateWindow(); - if (m_configureNotifyPending) - m_deferredExpose = true; - else - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size() * int(devicePixelRatio()))); + + QRegion exposeRegion = QRect(QPoint(), geometry().size()); + compressExposeEvent(exposeRegion); + QWindowSystemInterface::handleExposeEvent(window(), exposeRegion); } } @@ -2150,9 +2123,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); } } - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); if (isWheel) { if (!connection()->isAtLeastXI21()) { @@ -2174,9 +2146,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y, int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp) { - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); if (detail >= 4 && detail <= 7) { // mouse wheel, handled in handleButtonPressEvent() @@ -2189,11 +2160,8 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp) { - if (!xcbScreen()) - return; - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); handleMouseEvent(timestamp, local, global, modifiers); } @@ -2321,11 +2289,8 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) if (ignoreEnterEvent(event)) return; - const int dpr = int(devicePixelRatio()); - const QPoint local(event->event_x/dpr, event->event_y/dpr); - if (!xcbScreen()) - return; - QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); + const QPoint local(event->event_x, event->event_y); + QPoint global = QPoint(event->root_x, event->root_y); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -2341,11 +2306,8 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0; if (enterWindow) { - const int dpr = int(devicePixelRatio()); - QPoint local(enter->event_x/dpr, enter->event_y/dpr); - if (!xcbScreen()) - return; - QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); + 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 { @@ -2360,8 +2322,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev connection()->setTime(event->time); const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - if (!xcbScreen()) - return; if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { if (propertyDeleted) @@ -2399,12 +2359,12 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev QWindowSystemInterface::handleWindowStateChanged(window(), newState); m_lastWindowStateEvent = newState; m_windowState = newState; + if (m_windowState == Qt::WindowMinimized && connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); } return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; - } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && xcbScreen() && event->window == xcbScreen()->root()) { - xcbScreen()->updateGeometry(event->time); } } @@ -2455,9 +2415,15 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab) bool QXcbWindow::setMouseGrabEnabled(bool grab) { + if (!grab && connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); #ifdef XCB_USE_XINPUT22 - if (connection()->xi2MouseEvents()) - return connection()->xi2SetMouseGrabEnabled(m_window, grab); + if (connection()->xi2MouseEvents()) { + bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab); + if (grab && result) + connection()->setMouseGrabber(this); + return result; + } #endif if (grab && !connection()->canGrab()) return false; @@ -2476,6 +2442,8 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab) xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, NULL); bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS); free(reply); + if (result) + connection()->setMouseGrabber(this); return result; } @@ -2524,7 +2492,7 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) xev.type = moveResize; xev.window = xcb_window(); xev.format = 32; - const QPoint globalPos = mapToNative(window()->mapToGlobal(pos), xcbScreen()); + 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; @@ -2650,10 +2618,11 @@ void QXcbWindow::setMask(const QRegion ®ion) xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE); } else { - const int dpr = devicePixelRatio(); QVector<xcb_rectangle_t> rects; - foreach (const QRect &r, region.rects()) - rects.push_back(qRectToXCBRectangle(mapLocalGeometryToNative(r, dpr))); + const QVector<QRect> regionRects = region.rects(); + rects.reserve(regionRects.count()); + foreach (const QRect &r, regionRects) + rects.push_back(qRectToXCBRectangle(r)); xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, xcb_window(), 0, 0, rects.size(), &rects[0]); @@ -2682,8 +2651,6 @@ bool QXcbWindow::needsSync() const void QXcbWindow::postSyncWindowRequest() { - if (!xcbScreen()) - return; if (!m_pendingSyncRequest) { QXcbSyncWindowRequest *e = new QXcbSyncWindowRequest(this); m_pendingSyncRequest = e; @@ -2691,11 +2658,6 @@ void QXcbWindow::postSyncWindowRequest() } } -qreal QXcbWindow::devicePixelRatio() const -{ - return xcbScreen() ? xcbScreen()->devicePixelRatio() : 1.0; -} - QXcbScreen *QXcbWindow::xcbScreen() const { return static_cast<QXcbScreen *>(screen()); diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index a379a6f9db..d2c02fe3df 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -81,11 +81,12 @@ public: void setParent(const QPlatformWindow *window) Q_DECL_OVERRIDE; bool isExposed() const Q_DECL_OVERRIDE; - bool isEmbedded(const QPlatformWindow *parentWindow) const Q_DECL_OVERRIDE; + bool isEmbedded(const QPlatformWindow *parentWindow = 0) const Q_DECL_OVERRIDE; QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; void setWindowTitle(const QString &title) Q_DECL_OVERRIDE; + void setWindowIconText(const QString &title); void setWindowIcon(const QIcon &icon) Q_DECL_OVERRIDE; void raise() Q_DECL_OVERRIDE; void lower() Q_DECL_OVERRIDE; @@ -145,6 +146,16 @@ public: QXcbWindowFunctions::WmWindowTypes wmWindowTypes() const; void setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::WindowFlags flags); + static void setWindowIconTextStatic(QWindow *window, const QString &text); + + static void setParentRelativeBackPixmapStatic(QWindow *window); + void setParentRelativeBackPixmap(); + + static bool requestSystemTrayWindowDockStatic(const QWindow *window); + bool requestSystemTrayWindowDock() const; + + static QRect systemTrayWindowGlobalGeometryStatic(const QWindow *window); + QRect systemTrayWindowGlobalGeometry() const; uint visualId() const; bool needsSync() const; @@ -152,14 +163,10 @@ public: void postSyncWindowRequest(); void clearSyncWindowRequest() { m_pendingSyncRequest = 0; } - qreal devicePixelRatio() const Q_DECL_OVERRIDE; - QXcbScreen *xcbScreen() const; virtual void create(); virtual void destroy(); - void maybeSetScreen(QXcbScreen *screen); - QXcbScreen *screenForNativeGeometry(const QRect &newGeometry) const; public Q_SLOTS: void updateSyncRequestCounter(); @@ -167,12 +174,7 @@ public Q_SLOTS: protected: virtual void resolveFormat() { m_format = window()->requestedFormat(); } virtual void *createVisual() { return Q_NULLPTR; } - virtual bool supportsSyncProtocol() { return !window()->supportsOpenGL(); } - QPoint mapToNative(const QPoint &pos, const QXcbScreen *screen) const; - QPoint mapFromNative(const QPoint &pos, const QXcbScreen *screen) const; - QRect mapToNative(const QRect &rect, const QXcbScreen *screen) const; - QRect mapFromNative(const QRect &rect, const QXcbScreen *screen) const; QXcbScreen *parentScreen(); void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0); @@ -199,6 +201,8 @@ protected: void doFocusIn(); void doFocusOut(); + bool compressExposeEvent(QRegion &exposeRegion); + void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y, int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp); @@ -210,8 +214,6 @@ protected: xcb_window_t m_window; - QXcbScreen *m_xcbScreen; - uint m_depth; QImage::Format m_imageFormat; bool m_imageRgbSwap; @@ -227,8 +229,6 @@ protected: bool m_transparent; bool m_usingSyncProtocol; bool m_deferredActivation; - bool m_deferredExpose; - bool m_configureNotifyPending; bool m_embedded; bool m_alertState; xcb_window_t m_netWmUserTimeWindow; diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp index 7d31ac7118..82c1c1de77 100644 --- a/src/plugins/platforms/xcb/qxcbwmsupport.cpp +++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp @@ -94,14 +94,14 @@ void QXcbWMSupport::updateVirtualRoots() int offset = 0; int remaining = 0; do { - xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_ATOM, offset, 1024); + xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024); xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL); if (!reply) break; remaining = 0; - if (reply->type == XCB_ATOM_ATOM && reply->format == 32) { + if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) { int len = xcb_get_property_value_length(reply)/sizeof(xcb_window_t); xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply); int s = net_virtual_roots.size(); diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp index 46cee5d6d0..effdbf7334 100644 --- a/src/plugins/platforms/xcb/qxcbxsettings.cpp +++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp @@ -147,7 +147,7 @@ public: return; char byteOrder = xSettings.at(0); if (byteOrder != LSBFirst && byteOrder != MSBFirst) { - qWarning("%s ByteOrder byte %d not 0 or 1", Q_FUNC_INFO , byteOrder); + qWarning("ByteOrder byte %d not 0 or 1", byteOrder); return; } @@ -157,7 +157,7 @@ public: qFromBigEndian<t>((const uchar *)(x))) #define VALIDATE_LENGTH(x) \ if ((size_t)xSettings.length() < (offset + local_offset + 12 + x)) { \ - qWarning("%s Length %d runs past end of data", Q_FUNC_INFO , x); \ + qWarning("Length %d runs past end of data", x); \ return; \ } diff --git a/src/plugins/platforms/xcb/qxlibconvenience.cpp b/src/plugins/platforms/xcb/qxlibconvenience.cpp deleted file mode 100644 index f3c7d2b24e..0000000000 --- a/src/plugins/platforms/xcb/qxlibconvenience.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef XCB_USE_XLIB - -#include "qxlibconvenience.h" - -// Some Xlib headers are heavy macro namespace polluters and conflict with Qt types. -// This unit makes it easier to deal with them by encapsulating these includes in this .cpp. -#include <X11/Xutil.h> - -QT_BEGIN_NAMESPACE - -xcb_keysym_t q_XLookupString(void *display, xcb_window_t window, xcb_window_t root, uint state, xcb_keycode_t code, int type, QByteArray *chars) -{ - KeySym sym = 0; - chars->resize(512); - XKeyEvent event; - memset(&event, 0, sizeof(event)); - event.type = type; - event.display = static_cast<Display*>(display); - event.window = window; - event.root = root; - event.state = state; - event.keycode = code; - int count = XLookupString(&event, chars->data(), chars->size(), &sym, 0); - chars->resize(count); - return sym; -} - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/xcb/qxlibconvenience.h b/src/plugins/platforms/xcb/qxlibconvenience.h deleted file mode 100644 index 0e6e1c37ec..0000000000 --- a/src/plugins/platforms/xcb/qxlibconvenience.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef XLIBUTILS_H -#define XLIBUTILS_H - -#ifdef XCB_USE_XLIB - -#include <xcb/xcb_keysyms.h> -#include <QByteArray> - -QT_BEGIN_NAMESPACE - -xcb_keysym_t q_XLookupString(void *display, xcb_window_t window, xcb_window_t root, uint state, xcb_keycode_t code, int type, QByteArray *chars); - -QT_END_NAMESPACE - -#endif // XCB_USE_XLIB -#endif diff --git a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro index d0fe282b14..20481e4834 100644 --- a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro +++ b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro @@ -1,7 +1,8 @@ # # Statically compile in code for # libxcb-fixes, libxcb-randr, libxcb-shm, libxcb-sync, libxcb-image, -# libxcb-keysyms, libxcb-icccm, libxcb-renderutil, libxcb-xkb +# libxcb-keysyms, libxcb-icccm, libxcb-renderutil, libxcb-xkb, +# libxcb-xinerama # CONFIG += static load(qt_helper_lib) @@ -28,7 +29,8 @@ SOURCES += \ $$LIBXCB_DIR/sync.c \ $$LIBXCB_DIR/render.c \ $$LIBXCB_DIR/shape.c \ - $$LIBXCB_DIR/xkb.c + $$LIBXCB_DIR/xkb.c \ + $$LIBXCB_DIR/xinerama.c # # xcb-util diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 12987567ff..302d87e007 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -1,13 +1,6 @@ TARGET = QtXcbQpa CONFIG += no_module_headers internal_module -MODULE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE \ - \$\$QT_MODULE_INCLUDE_BASE/QtQGui -MODULE_PRIVATE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION/QtGui - load(qt_module) QT += core-private gui-private platformsupport-private @@ -47,8 +40,6 @@ HEADERS = \ qxcbxsettings.h \ qxcbsystemtraytracker.h -LIBS += $$QMAKE_LIBS_DYNLOAD - DEFINES += QT_BUILD_XCB_PLUGIN # needed by Xcursor ... contains(QT_CONFIG, xcb-xlib) { @@ -101,7 +92,7 @@ contains(QT_CONFIG, xcb-qt) { INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude LIBS += -lxcb -L$$OUT_PWD/xcb-static -lxcb-static } else { - LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms -lxcb-xinerama !contains(DEFINES, QT_NO_XKB):LIBS += -lxcb-xkb } |