diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
34 files changed, 6187 insertions, 558 deletions
diff --git a/src/plugins/platforms/xcb/README b/src/plugins/platforms/xcb/README index 17a86e6d08..d2884f109c 100644 --- a/src/plugins/platforms/xcb/README +++ b/src/plugins/platforms/xcb/README @@ -1,3 +1,3 @@ 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 +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 diff --git a/src/plugins/platforms/xcb/main.cpp b/src/plugins/platforms/xcb/main.cpp index 7fee7455be..c544f7073d 100644 --- a/src/plugins/platforms/xcb/main.cpp +++ b/src/plugins/platforms/xcb/main.cpp @@ -58,11 +58,10 @@ QStringList QXcbIntegrationPlugin::keys() const return list; } -QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& paramList) +QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters) { - Q_UNUSED(paramList); if (system.toLower() == "xcb") - return new QXcbIntegration; + return new QXcbIntegration(parameters); return 0; } diff --git a/src/plugins/platforms/xcb/qdri2context.cpp b/src/plugins/platforms/xcb/qdri2context.cpp index 8bcdacb92b..5f4aff5455 100644 --- a/src/plugins/platforms/xcb/qdri2context.cpp +++ b/src/plugins/platforms/xcb/qdri2context.cpp @@ -45,7 +45,7 @@ #include "qxcbconnection.h" #include <QtCore/QDebug> -#include <QtGui/QWidget> +#include <QtWidgets/QWidget> #include <xcb/dri2.h> #include <xcb/xfixes.h> @@ -134,9 +134,9 @@ QDri2Context::QDri2Context(QXcbWindow *window) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERER,d->depth); //restore the old current context - const QPlatformGLContext *currentContext = QPlatformGLContext::currentContext(); + const QPlatformOpenGLContext *currentContext = QPlatformOpenGLContext::currentContext(); if (currentContext) - const_cast<QPlatformGLContext*>(currentContext)->makeCurrent(); + const_cast<QPlatformOpenGLContext*>(currentContext)->makeCurrent(); } QDri2Context::~QDri2Context() @@ -146,7 +146,6 @@ QDri2Context::~QDri2Context() void QDri2Context::makeCurrent() { - QPlatformGLContext::makeCurrent(); Q_D(QDri2Context); eglMakeCurrent(EGL_DISPLAY_FROM_XCB(d->qXcbWindow),EGL_NO_SURFACE,EGL_NO_SURFACE,d->eglContext); @@ -156,7 +155,6 @@ void QDri2Context::makeCurrent() void QDri2Context::doneCurrent() { - QPlatformGLContext::doneCurrent(); Q_D(QDri2Context); eglMakeCurrent(EGL_DISPLAY_FROM_XCB(d->qXcbWindow),EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); } diff --git a/src/plugins/platforms/xcb/qdri2context.h b/src/plugins/platforms/xcb/qdri2context.h index be7d637643..ec8b251c3f 100644 --- a/src/plugins/platforms/xcb/qdri2context.h +++ b/src/plugins/platforms/xcb/qdri2context.h @@ -42,14 +42,14 @@ #ifndef QDRI2CONTEXT_H #define QDRI2CONTEXT_H -#include <QtGui/QPlatformGLContext> +#include <QtGui/QPlatformOpenGLContext> class QXcbWindow; class QDri2ContextPrivate; struct xcb_dri2_dri2_buffer_t; -class QDri2Context : public QPlatformGLContext +class QDri2Context : public QPlatformOpenGLContext { Q_DECLARE_PRIVATE(QDri2Context); public: diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index 8e04cbcb71..41bceaf406 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -41,7 +41,6 @@ #include <QDebug> #include <QLibrary> -#include <QGLFormat> #include "qxcbwindow.h" #include "qxcbscreen.h" @@ -50,72 +49,56 @@ #include <X11/Xutil.h> #include <GL/glx.h> +#include <QtGui/QOpenGLContext> + #include "qglxintegration.h" -#include "qglxconvenience.h" +#include <QtPlatformSupport/private/qglxconvenience_p.h> #if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) #include <dlfcn.h> #endif -QGLXContext::QGLXContext(Window window, QXcbScreen *screen, const QPlatformWindowFormat &format) - : QPlatformGLContext() +QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share) + : QPlatformOpenGLContext() , m_screen(screen) - , m_drawable((Drawable)window) , m_context(0) { - Q_XCB_NOOP(m_screen->connection()); - const QPlatformGLContext *sharePlatformContext; - sharePlatformContext = format.sharedGLContext(); GLXContext shareGlxContext = 0; - if (sharePlatformContext) - shareGlxContext = static_cast<const QGLXContext*>(sharePlatformContext)->glxContext(); + if (share) + shareGlxContext = static_cast<const QGLXContext*>(share)->glxContext(); GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),format); m_context = glXCreateNewContext(DISPLAY_FROM_XCB(screen), config, GLX_RGBA_TYPE, shareGlxContext, TRUE); - m_windowFormat = qglx_platformWindowFromGLXFBConfig(DISPLAY_FROM_XCB(screen), config, m_context); - Q_XCB_NOOP(m_screen->connection()); -} - -QGLXContext::QGLXContext(QXcbScreen *screen, Drawable drawable, GLXContext context) - : QPlatformGLContext(), m_screen(screen), m_drawable(drawable), m_context(context) -{ - + m_format = qglx_surfaceFormatFromGLXFBConfig(DISPLAY_FROM_XCB(screen), config, m_context); } QGLXContext::~QGLXContext() { - Q_XCB_NOOP(m_screen->connection()); - if (m_context) - glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context); - Q_XCB_NOOP(m_screen->connection()); + glXDestroyContext(DISPLAY_FROM_XCB(m_screen), m_context); } -void QGLXContext::makeCurrent() +bool QGLXContext::makeCurrent(QPlatformSurface *surface) { - Q_XCB_NOOP(m_screen->connection()); - QPlatformGLContext::makeCurrent(); - glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), m_drawable, m_context); - Q_XCB_NOOP(m_screen->connection()); + Q_ASSERT(surface); + + GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); + + return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), glxDrawable, m_context); } void QGLXContext::doneCurrent() { - Q_XCB_NOOP(m_screen->connection()); - QPlatformGLContext::doneCurrent(); glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0); - Q_XCB_NOOP(m_screen->connection()); } -void QGLXContext::swapBuffers() +void QGLXContext::swapBuffers(QPlatformSurface *surface) { - Q_XCB_NOOP(m_screen->connection()); - glXSwapBuffers(DISPLAY_FROM_XCB(m_screen), m_drawable); - Q_XCB_NOOP(m_screen->connection()); + GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); + glXSwapBuffers(DISPLAY_FROM_XCB(m_screen), glxDrawable); } -void* QGLXContext::getProcAddress(const QString& procName) +void (*QGLXContext::getProcAddress(const QByteArray &procName)) () { - Q_XCB_NOOP(m_screen->connection()); typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *); static qt_glXGetProcAddressARB glXGetProcAddressARB = 0; static bool resolved = false; @@ -144,10 +127,10 @@ void* QGLXContext::getProcAddress(const QString& procName) } if (!glXGetProcAddressARB) return 0; - return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.toLatin1().data())); + return (void (*)())glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.constData())); } -QPlatformWindowFormat QGLXContext::platformWindowFormat() const +QSurfaceFormat QGLXContext::format() const { - return m_windowFormat; + return m_format; } diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h index 84de7b7143..bbe67b653a 100644 --- a/src/plugins/platforms/xcb/qglxintegration.h +++ b/src/plugins/platforms/xcb/qglxintegration.h @@ -43,36 +43,34 @@ #define QGLXINTEGRATION_H #include "qxcbwindow.h" +#include "qxcbscreen.h" -#include <QtGui/QPlatformGLContext> -#include <QtGui/QPlatformWindowFormat> +#include <QtGui/QPlatformOpenGLContext> +#include <QtGui/QSurfaceFormat> #include <QtCore/QMutex> #include <GL/glx.h> -class QGLXContext : public QPlatformGLContext +class QGLXContext : public QPlatformOpenGLContext { public: - QGLXContext(Window window, QXcbScreen *xd, const QPlatformWindowFormat &format); + QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share); ~QGLXContext(); - virtual void makeCurrent(); - virtual void doneCurrent(); - virtual void swapBuffers(); - virtual void* getProcAddress(const QString& procName); + bool makeCurrent(QPlatformSurface *surface); + void doneCurrent(); + void swapBuffers(QPlatformSurface *surface); + void (*getProcAddress(const QByteArray &procName)) (); - GLXContext glxContext() const { return m_context; } + QSurfaceFormat format() const; - QPlatformWindowFormat platformWindowFormat() const; + GLXContext glxContext() const { return m_context; } private: QXcbScreen *m_screen; - Drawable m_drawable; GLXContext m_context; - QPlatformWindowFormat m_windowFormat; - - QGLXContext (QXcbScreen *screen, Drawable drawable, GLXContext context); + QSurfaceFormat m_format; }; #endif diff --git a/src/plugins/platforms/xcb/qxcbwindowsurface.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 4fcd207df3..fadcb4d5b8 100644 --- a/src/plugins/platforms/xcb/qxcbwindowsurface.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "qxcbwindowsurface.h" +#include "qxcbbackingstore.h" #include "qxcbconnection.h" #include "qxcbscreen.h" @@ -52,9 +52,11 @@ #include <sys/shm.h> #include <stdio.h> +#include <errno.h> #include <qdebug.h> #include <qpainter.h> +#include <qscreen.h> class QXcbShmImage : public QXcbObject { @@ -63,6 +65,7 @@ public: ~QXcbShmImage() { destroy(); } QImage *image() { return &m_qimage; } + QSize size() const { return m_qimage.size(); } void put(xcb_window_t window, const QPoint &dst, const QRect &source); void preparePaint(const QRegion ®ion); @@ -97,30 +100,54 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI ~0, 0); - m_shm_info.shmid = shmget (IPC_PRIVATE, - m_xcb_image->stride * m_xcb_image->height, IPC_CREAT|0777); + const int segmentSize = m_xcb_image->stride * m_xcb_image->height; + if (!segmentSize) + return; + int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0777); + if (id == -1) + qWarning("QXcbShmImage: shmget() failed (%d) for size %d (%dx%d)", + 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); m_shm_info.shmseg = xcb_generate_id(xcb_connection()); xcb_generic_error_t *error = xcb_request_check(xcb_connection(), xcb_shm_attach_checked(xcb_connection(), m_shm_info.shmseg, m_shm_info.shmid, false)); if (error) { - qWarning() << "QXcbWindowSurface: Unable to attach to shared memory segment"; free(error); - } - if (shmctl(m_shm_info.shmid, IPC_RMID, 0) == -1) - qWarning() << "QXcbWindowSurface: Error while marking the shared memory segment to be destroyed"; + shmdt(m_shm_info.shmaddr); + shmctl(m_shm_info.shmid, IPC_RMID, 0); + + m_shm_info.shmaddr = 0; + + m_xcb_image->data = (uint8_t *)qMalloc(segmentSize); + } else { + if (shmctl(m_shm_info.shmid, IPC_RMID, 0) == -1) + qWarning() << "QXcbBackingStore: Error while marking the shared memory segment to be destroyed"; + } m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format); } void QXcbShmImage::destroy() { - Q_XCB_CALL(xcb_shm_detach(xcb_connection(), m_shm_info.shmseg)); + const int segmentSize = m_xcb_image ? (m_xcb_image->stride * m_xcb_image->height) : 0; + if (segmentSize && m_shm_info.shmaddr) + Q_XCB_CALL(xcb_shm_detach(xcb_connection(), m_shm_info.shmseg)); + xcb_image_destroy(m_xcb_image); - shmdt(m_shm_info.shmaddr); - shmctl(m_shm_info.shmid, IPC_RMID, 0); + + if (segmentSize) { + if (m_shm_info.shmaddr) { + shmdt(m_shm_info.shmaddr); + shmctl(m_shm_info.shmid, IPC_RMID, 0); + } else { + qFree(m_xcb_image->data); + } + } + if (m_gc) Q_XCB_CALL(xcb_free_gc(xcb_connection(), m_gc)); } @@ -139,18 +166,32 @@ void QXcbShmImage::put(xcb_window_t window, const QPoint &target, const QRect &s } Q_XCB_NOOP(connection()); - xcb_image_shm_put(xcb_connection(), + if (m_shm_info.shmaddr) { + xcb_image_shm_put(xcb_connection(), + window, + m_gc, + m_xcb_image, + m_shm_info, + source.x(), + source.y(), + target.x(), + target.y(), + source.width(), + source.height(), + false); + } else { + xcb_image_t *subimage = xcb_image_subimage(m_xcb_image, source.x(), source.y(), source.width(), source.height(), + 0, 0, 0); + xcb_image_put(xcb_connection(), window, m_gc, - m_xcb_image, - m_shm_info, - source.x(), - source.y(), + subimage, target.x(), target.y(), - source.width(), - source.height(), - false); + 0); + + xcb_image_destroy(subimage); + } Q_XCB_NOOP(connection()); m_dirty = m_dirty | source; @@ -168,29 +209,30 @@ void QXcbShmImage::preparePaint(const QRegion ®ion) } } -QXcbWindowSurface::QXcbWindowSurface(QWidget *widget, bool setDefaultSurface) - : QWindowSurface(widget, setDefaultSurface) +QXcbBackingStore::QXcbBackingStore(QWindow *window) + : QPlatformBackingStore(window) , m_image(0) , m_syncingResize(false) { - QXcbScreen *screen = static_cast<QXcbScreen *>(QPlatformScreen::platformScreenForWidget(widget)); + QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle()); setConnection(screen->connection()); } -QXcbWindowSurface::~QXcbWindowSurface() +QXcbBackingStore::~QXcbBackingStore() { delete m_image; } -QPaintDevice *QXcbWindowSurface::paintDevice() +QPaintDevice *QXcbBackingStore::paintDevice() { return m_image->image(); } -void QXcbWindowSurface::beginPaint(const QRegion ®ion) +void QXcbBackingStore::beginPaint(const QRegion ®ion) { m_image->preparePaint(region); +#if 0 if (m_image->image()->hasAlphaChannel()) { QPainter p(m_image->image()); p.setCompositionMode(QPainter::CompositionMode_Source); @@ -200,29 +242,27 @@ void QXcbWindowSurface::beginPaint(const QRegion ®ion) p.fillRect(*it, blank); } } +#endif } -void QXcbWindowSurface::endPaint(const QRegion &) +void QXcbBackingStore::endPaint(const QRegion &) { } -void QXcbWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { QRect bounds = region.boundingRect(); - if (size().isEmpty() || !geometry().contains(bounds)) + if (!m_image || m_image->size().isEmpty()) return; Q_XCB_NOOP(connection()); - QXcbWindow *window = static_cast<QXcbWindow *>(widget->window()->platformWindow()); - - extern QWidgetData* qt_widget_data(QWidget *); - QPoint widgetOffset = qt_qwidget_data(widget)->wrect.topLeft(); + QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); QVector<QRect> rects = region.rects(); for (int i = 0; i < rects.size(); ++i) - m_image->put(window->window(), rects.at(i).topLeft() - widgetOffset, rects.at(i).translated(offset)); + m_image->put(platformWindow->xcb_window(), rects.at(i).topLeft(), rects.at(i).translated(offset)); Q_XCB_NOOP(connection()); @@ -230,23 +270,22 @@ void QXcbWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoi xcb_flush(xcb_connection()); connection()->sync(); m_syncingResize = false; - window->updateSyncRequestCounter(); + platformWindow->updateSyncRequestCounter(); } } -void QXcbWindowSurface::resize(const QSize &size) +void QXcbBackingStore::resize(const QSize &size, const QRegion &) { - if (size == QWindowSurface::size()) + if (m_image && size == m_image->size()) return; Q_XCB_NOOP(connection()); - QWindowSurface::resize(size); - QXcbScreen *screen = static_cast<QXcbScreen *>(QPlatformScreen::platformScreenForWidget(window())); - QXcbWindow* win = static_cast<QXcbWindow *>(window()->platformWindow()); + QXcbScreen *screen = static_cast<QXcbScreen *>(window()->screen()->handle()); + QXcbWindow* win = static_cast<QXcbWindow *>(window()->handle()); delete m_image; - m_image = new QXcbShmImage(screen, size, win->depth(), win->format()); + m_image = new QXcbShmImage(screen, size, win->depth(), win->imageFormat()); Q_XCB_NOOP(connection()); m_syncingResize = true; @@ -254,9 +293,9 @@ void QXcbWindowSurface::resize(const QSize &size) extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); -bool QXcbWindowSurface::scroll(const QRegion &area, int dx, int dy) +bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy) { - if (m_image->image()->isNull()) + if (!m_image || m_image->image()->isNull()) return false; m_image->preparePaint(area); diff --git a/src/plugins/platforms/xcb/qxcbwindowsurface.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index 5f61815b65..db94d26b09 100644 --- a/src/plugins/platforms/xcb/qxcbwindowsurface.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -39,10 +39,10 @@ ** ****************************************************************************/ -#ifndef QXCBWINDOWSURFACE_H -#define QXCBWINDOWSURFACE_H +#ifndef QXCBBACKINGSTORE_H +#define QXCBBACKINGSTORE_H -#include <private/qwindowsurface_p.h> +#include <qplatformbackingstore_qpa.h> #include <xcb/xcb.h> @@ -50,15 +50,15 @@ class QXcbShmImage; -class QXcbWindowSurface : public QXcbObject, public QWindowSurface +class QXcbBackingStore : public QXcbObject, public QPlatformBackingStore { public: - QXcbWindowSurface(QWidget *widget, bool setDefaultSurface = true); - ~QXcbWindowSurface(); + QXcbBackingStore(QWindow *widget); + ~QXcbBackingStore(); QPaintDevice *paintDevice(); - void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); - void resize(const QSize &size); + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size, const QRegion &staticContents); bool scroll(const QRegion &area, int dx, int dy); void beginPaint(const QRegion &); diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp new file mode 100644 index 0000000000..550b8cbd73 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -0,0 +1,848 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbclipboard.h" + +#include "qxcbconnection.h" +#include "qxcbscreen.h" +#include "qxcbmime.h" + +#include <private/qguiapplication_p.h> +#include <QElapsedTimer> + +#include <QtCore/QDebug> + +#include <xcb/xcb_icccm.h> + +class QXcbClipboardMime : public QXcbMime +{ + Q_OBJECT +public: + QXcbClipboardMime(QClipboard::Mode mode, QXcbClipboard *clipboard) + : QXcbMime() + , m_clipboard(clipboard) + { + switch (mode) { + case QClipboard::Selection: + modeAtom = XCB_ATOM_PRIMARY; + break; + + case QClipboard::Clipboard: + modeAtom = m_clipboard->atom(QXcbAtom::CLIPBOARD); + break; + + default: + qWarning("QTestLiteMime: Internal error: Unsupported clipboard mode"); + break; + } + } + +protected: + QStringList formats_sys() const + { + if (empty()) + return QStringList(); + + if (!formatList.count()) { + QXcbClipboardMime *that = const_cast<QXcbClipboardMime *>(this); + // get the list of targets from the current clipboard owner - we do this + // once so that multiple calls to this function don't require multiple + // server round trips... + that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::TARGETS)); + + if (format_atoms.size() > 0) { + xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data(); + int size = format_atoms.size() / sizeof(xcb_atom_t); + + for (int i = 0; i < size; ++i) { + if (targets[i] == 0) + continue; + + QString format = mimeAtomToString(m_clipboard->connection(), targets[i]); + if (!formatList.contains(format)) + that->formatList.append(format); + } + } + } + + return formatList; + } + + bool hasFormat_sys(const QString &format) const + { + QStringList list = formats(); + return list.contains(format); + } + + QVariant retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const + { + if (fmt.isEmpty() || empty()) + return QByteArray(); + + (void)formats(); // trigger update of format list + + QList<xcb_atom_t> atoms; + xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data(); + int size = format_atoms.size() / sizeof(xcb_atom_t); + for (int i = 0; i < size; ++i) + atoms.append(targets[i]); + + QByteArray encoding; + xcb_atom_t fmtatom = mimeAtomForFormat(m_clipboard->connection(), fmt, requestedType, atoms, &encoding); + + if (fmtatom == 0) + return QVariant(); + + return mimeConvertToFormat(m_clipboard->connection(), fmtatom, m_clipboard->getDataInFormat(modeAtom, fmtatom), fmt, requestedType, encoding); + } +private: + bool empty() const + { + return m_clipboard->getSelectionOwner(modeAtom) == XCB_NONE; + } + + + xcb_atom_t modeAtom; + QXcbClipboard *m_clipboard; + QStringList formatList; + QByteArray format_atoms; +}; + +const int QXcbClipboard::clipboard_timeout = 5000; + +QXcbClipboard::QXcbClipboard(QXcbConnection *c) + : QXcbObject(c), QPlatformClipboard() + , m_requestor(XCB_NONE) + , m_owner(XCB_NONE) +{ + Q_ASSERT(QClipboard::Clipboard == 0); + Q_ASSERT(QClipboard::Selection == 1); + m_xClipboard[QClipboard::Clipboard] = 0; + m_xClipboard[QClipboard::Selection] = 0; + m_clientClipboard[QClipboard::Clipboard] = 0; + m_clientClipboard[QClipboard::Selection] = 0; + m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME; + m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME; + + m_screen = connection()->screens().at(connection()->primaryScreen()); + + 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 + m_screen->screen()->root, // parent window id + x, y, w, h, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + m_screen->screen()->root_visual, // visual + 0, // value mask + 0)); // value list + + 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(), m_owner, XCB_ATOM_PRIMARY, mask)); + Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask)); + } +} + +QXcbClipboard::~QXcbClipboard() +{ + // Transfer the clipboard content to the clipboard manager if we own a selection + if (m_timestamp[QClipboard::Clipboard] != XCB_CURRENT_TIME || + m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) { + + // First we check if there is a clipboard manager. + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER)); + xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0); + if (reply && reply->owner != XCB_NONE) { + // we delete the property so the manager saves all TARGETS. + xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION)); + xcb_convert_selection(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS), + atom(QXcbAtom::_QT_SELECTION), connection()->time()); + connection()->sync(); + + // waiting until the clipboard manager fetches the content. + if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, 5000)) { + qWarning("QClipboard: Unable to receive an event from the " + "clipboard manager in a reasonable time"); + } + } + } +} + + +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; +} + +xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const +{ + if (mode == QClipboard::Clipboard) + return atom(QXcbAtom::CLIPBOARD); + if (mode == QClipboard::Selection) + return XCB_ATOM_PRIMARY; + return XCB_NONE; +} + +QClipboard::Mode QXcbClipboard::modeForAtom(xcb_atom_t a) const +{ + if (a == XCB_ATOM_PRIMARY) + return QClipboard::Selection; + if (a == atom(QXcbAtom::CLIPBOARD)) + return QClipboard::Clipboard; + // not supported enum value, used to detect errors + return QClipboard::FindBuffer; +} + + +QMimeData * QXcbClipboard::mimeData(QClipboard::Mode mode) +{ + if (mode > QClipboard::Selection) + return 0; + + xcb_window_t clipboardOwner = getSelectionOwner(atomForMode(mode)); + if (clipboardOwner == owner()) { + return m_clientClipboard[mode]; + } else { + if (!m_xClipboard[mode]) + m_xClipboard[mode] = new QXcbClipboardMime(mode, this); + + return m_xClipboard[mode]; + } +} + +void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + if (mode > QClipboard::Selection) + return; + + xcb_atom_t modeAtom = atomForMode(mode); + + if (m_clientClipboard[mode] == data) + return; + + xcb_window_t newOwner = XCB_NONE; + + if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) + delete m_clientClipboard[mode]; + m_clientClipboard[mode] = 0; + m_timestamp[mode] = XCB_CURRENT_TIME; + + if (data) { + newOwner = owner(); + + m_clientClipboard[mode] = data; + m_timestamp[mode] = connection()->time(); + } + + xcb_set_selection_owner(xcb_connection(), newOwner, modeAtom, connection()->time()); + + if (getSelectionOwner(modeAtom) != newOwner) { + qWarning("QClipboard::setData: Cannot set X11 selection owner"); + } + +} + +bool QXcbClipboard::supportsMode(QClipboard::Mode mode) const +{ + if (mode <= QClipboard::Selection) + return true; + return false; +} + +bool QXcbClipboard::ownsMode(QClipboard::Mode mode) const +{ + if (m_owner == XCB_NONE || mode > QClipboard::Selection) + return false; + + Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME || getSelectionOwner(atomForMode(mode)) == m_owner); + + return m_timestamp[mode] != XCB_CURRENT_TIME; +} + +xcb_window_t QXcbClipboard::requestor() const +{ + if (!m_requestor) { + const int x = 0, y = 0, w = 3, h = 3; + QXcbClipboard *that = const_cast<QXcbClipboard *>(this); + + xcb_window_t window = xcb_generate_id(xcb_connection()); + Q_XCB_CALL(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, // depth -- same as root + window, // window id + m_screen->screen()->root, // parent window id + x, y, w, h, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + m_screen->screen()->root_visual, // visual + 0, // value mask + 0)); // value list + + uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(xcb_connection(), window, XCB_CW_EVENT_MASK, &mask); + + that->setRequestor(window); + } + return m_requestor; +} + +void QXcbClipboard::setRequestor(xcb_window_t window) +{ + if (m_requestor != XCB_NONE) { + xcb_destroy_window(xcb_connection(), m_requestor); + } + m_requestor = window; +} + +xcb_window_t QXcbClipboard::owner() const +{ + return m_owner; +} + +xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property) +{ + QVector<xcb_atom_t> types; + QStringList formats = QInternalMimeData::formatsHelper(d); + for (int i = 0; i < formats.size(); ++i) { + QList<xcb_atom_t> atoms = QXcbMime::mimeAtomsForFormat(connection(), formats.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!types.contains(atoms.at(j))) + types.append(atoms.at(j)); + } + } + types.append(atom(QXcbAtom::TARGETS)); + types.append(atom(QXcbAtom::MULTIPLE)); + types.append(atom(QXcbAtom::TIMESTAMP)); + types.append(atom(QXcbAtom::SAVE_TARGETS)); + + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM, + 32, types.size(), (const void *)types.constData()); + return property; +} + +xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property) +{ + xcb_atom_t atomFormat = target; + int dataFormat = 0; + QByteArray data; + + QString fmt = QXcbMime::mimeAtomToString(connection(), target); + if (fmt.isEmpty()) { // Not a MIME type we have +// qDebug() << "QClipboard: send_selection(): converting to type" << connection()->atomName(target) << "is not supported"; + return XCB_NONE; + } +// qDebug() << "QClipboard: send_selection(): converting to type" << fmt; + + if (QXcbMime::mimeDataForAtom(connection(), target, d, &data, &atomFormat, &dataFormat)) { + + // don't allow INCR transfers when using MULTIPLE or to + // Motif clients (since Motif doesn't support INCR) + static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY); + bool allow_incr = property != motif_clip_temporary; + + // X_ChangeProperty protocol request is 24 bytes + const int increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24; + if (data.size() > increment && allow_incr) { + long bytes = data.size(); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, + atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes); + +// (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment); + qWarning() << "not implemented INCR just YET!"; + return property; + } + + // make sure we can perform the XChangeProperty in a single request + if (data.size() > increment) + return XCB_NONE; // ### perhaps use several XChangeProperty calls w/ PropModeAppend? + int dataSize = data.size() / (dataFormat / 8); + // use a single request to transfer data + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, atomFormat, + dataFormat, dataSize, (const void *)data.constData()); + } + return property; +} + +void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *event) +{ + QClipboard::Mode mode = modeForAtom(event->selection); + if (mode > QClipboard::Selection) + return; + + // ignore the event if it was generated before we gained selection ownership + if (m_timestamp[mode] != XCB_CURRENT_TIME && event->time <= m_timestamp[mode]) + return; + +// DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)", +// XGetSelectionOwner(dpy, XA_PRIMARY), +// xevent->xselectionclear.time, d->timestamp); + +// if (!waiting_for_data) { + setMimeData(0, mode); + emitChanged(mode); +// } else { +// pending_selection_changed = true; +// if (! pending_timer_id) +// pending_timer_id = QApplication::clipboard()->startTimer(0); +// } +} + +void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) +{ + if (requestor() && req->requestor == requestor()) { + qDebug() << "This should be caught before"; + return; + } + + xcb_selection_notify_event_t event; + event.response_type = XCB_SELECTION_NOTIFY; + event.requestor = req->requestor; + event.selection = req->selection; + event.target = req->target; + event.property = XCB_NONE; + event.time = req->time; + + QMimeData *d; + QClipboard::Mode mode = modeForAtom(req->selection); + if (mode > QClipboard::Selection) { + qWarning() << "QClipboard: Unknown selection" << connection()->atomName(req->selection); + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + return; + } + + d = m_clientClipboard[mode]; + + if (!d) { + qWarning("QClipboard: Cannot transfer data, no data available"); + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + return; + } + + if (m_timestamp[mode] == XCB_CURRENT_TIME // we don't own the selection anymore + || (req->time != XCB_CURRENT_TIME && req->time < m_timestamp[mode])) { + qDebug("QClipboard: SelectionRequest too old"); + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + return; + } + + xcb_atom_t xa_targets = atom(QXcbAtom::TARGETS); + xcb_atom_t xa_multiple = atom(QXcbAtom::MULTIPLE); + xcb_atom_t xa_timestamp = atom(QXcbAtom::TIMESTAMP); + + struct AtomPair { xcb_atom_t target; xcb_atom_t property; } *multi = 0; + xcb_atom_t multi_type = XCB_NONE; + int multi_format = 0; + int nmulti = 0; + int imulti = -1; + bool multi_writeback = false; + + if (req->target == xa_multiple) { + QByteArray multi_data; + if (req->property == XCB_NONE + || !clipboardReadProperty(req->requestor, req->property, false, &multi_data, + 0, &multi_type, &multi_format) + || multi_format != 32) { + // MULTIPLE property not formatted correctly + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + return; + } + nmulti = multi_data.size()/sizeof(*multi); + multi = new AtomPair[nmulti]; + memcpy(multi,multi_data.data(),multi_data.size()); + imulti = 0; + } + + for (; imulti < nmulti; ++imulti) { + xcb_atom_t target; + xcb_atom_t property; + + if (multi) { + target = multi[imulti].target; + property = multi[imulti].property; + } else { + target = req->target; + property = req->property; + if (property == XCB_NONE) // obsolete client + property = target; + } + + xcb_atom_t ret = XCB_NONE; + if (target == XCB_NONE || property == XCB_NONE) { + ; + } else if (target == xa_timestamp) { + if (m_timestamp[mode] != XCB_CURRENT_TIME) { + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, req->requestor, + property, XCB_ATOM_INTEGER, 32, 1, &m_timestamp[mode]); + ret = property; + } else { + qWarning("QClipboard: Invalid data timestamp"); + } + } else if (target == xa_targets) { + ret = sendTargetsSelection(d, req->requestor, property); + } else { + ret = sendSelection(d, target, req->requestor, property); + } + + if (nmulti > 0) { + if (ret == XCB_NONE) { + multi[imulti].property = XCB_NONE; + multi_writeback = true; + } + } else { + event.property = ret; + break; + } + } + + if (nmulti > 0) { + if (multi_writeback) { + // according to ICCCM 2.6.2 says to put None back + // into the original property on the requestor window + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, req->requestor, req->property, + multi_type, 32, nmulti*2, (const void *)multi); + } + + delete [] multi; + event.property = req->property; + } + + // send selection notify to requestor + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); +} + +void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event) +{ + QClipboard::Mode mode = modeForAtom(event->selection); + if (event->owner != owner() && m_clientClipboard[mode] && m_timestamp[mode] < event->selection_timestamp) { + setMimeData(0, mode); + emitChanged(mode); + } +} + + +static inline int maxSelectionIncr(xcb_connection_t *c) +{ + int l = xcb_get_maximum_request_length(c); + return (l > 65536 ? 65536*4 : l*4) - 100; +} + +bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const +{ + int maxsize = maxSelectionIncr(xcb_connection()); + ulong bytes_left; // bytes_after + xcb_atom_t dummy_type; + int dummy_format; + + if (!type) // allow null args + type = &dummy_type; + if (!format) + format = &dummy_format; + + // Don't read anything, just get the size of the property data + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0)); + xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0); + if (!reply || reply->type == XCB_NONE) { + buffer->resize(0); + return false; + } + *type = reply->type; + *format = reply->format; + bytes_left = reply->bytes_after; + free(reply); + + int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left; + + switch (*format) { + case 8: + default: + format_inc = sizeof(char) / 1; + break; + + case 16: + format_inc = sizeof(short) / 2; + proplen *= sizeof(short) / 2; + break; + + case 32: + format_inc = sizeof(long) / 4; + proplen *= sizeof(long) / 4; + break; + } + + int newSize = proplen; + buffer->resize(newSize); + + bool ok = (buffer->size() == newSize); + + if (ok && newSize) { + // could allocate buffer + + while (bytes_left) { + // more to read... + + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4)); + reply = xcb_get_property_reply(xcb_connection(), cookie, 0); + if (!reply || reply->type == XCB_NONE) { + free(reply); + break; + } + *type = reply->type; + *format = reply->format; + bytes_left = reply->bytes_after; + char *data = (char *)xcb_get_property_value(reply); + int length = xcb_get_property_value_length(reply); + + offset += length / (32 / *format); + length *= format_inc * (*format) / 8; + + // Here we check if we get a buffer overflow and tries to + // recover -- this shouldn't normally happen, but it doesn't + // hurt to be defensive + if ((int)(buffer_offset + length) > buffer->size()) { + length = buffer->size() - buffer_offset; + + // escape loop + bytes_left = 0; + } + + memcpy(buffer->data() + buffer_offset, data, length); + buffer_offset += length; + + free(reply); + } + } + + + // correct size, not 0-term. + if (size) + *size = buffer_offset; + + if (deleteProperty) + xcb_delete_property(xcb_connection(), win, property); + + connection()->flush(); + + return ok; +} + + +namespace +{ + class Notify { + public: + Notify(xcb_window_t win, int t) + : window(win), type(t) {} + xcb_window_t window; + int type; + bool check(xcb_generic_event_t *event) const { + if (!event) + return false; + int t = event->response_type & 0x7f; + if (t != type) + return false; + if (t == XCB_PROPERTY_NOTIFY) { + xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event; + if (pn->window == window) + return true; + } else if (t == XCB_SELECTION_NOTIFY) { + xcb_selection_notify_event_t *sn = (xcb_selection_notify_event_t *)event; + if (sn->requestor == window) + return true; + } + return false; + } + }; + class ClipboardEvent { + public: + ClipboardEvent(QXcbConnection *c) + { clipboard = c->internAtom("CLIPBOARD"); } + xcb_atom_t clipboard; + bool check(xcb_generic_event_t *e) const { + if (!e) + return false; + int type = e->response_type & 0x7f; + if (type == XCB_SELECTION_REQUEST) { + xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)e; + return sr->selection == XCB_ATOM_PRIMARY || sr->selection == clipboard; + } else if (type == XCB_SELECTION_CLEAR) { + xcb_selection_clear_event_t *sc = (xcb_selection_clear_event_t *)e; + return sc->selection == XCB_ATOM_PRIMARY || sc->selection == clipboard; + } + return false; + } + }; +} + +xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int type, int timeout) +{ + QElapsedTimer timer; + timer.start(); + do { + Notify notify(win, type); + xcb_generic_event_t *e = connection()->checkEvent(notify); + if (e) + return e; + + // process other clipboard events, since someone is probably requesting data from us + ClipboardEvent clipboard(connection()); + e = connection()->checkEvent(clipboard); + if (e) { + connection()->handleXcbEvent(e); + free(e); + } + + connection()->flush(); + + // sleep 50 ms, so we don't use up CPU cycles all the time. + struct timeval usleep_tv; + usleep_tv.tv_sec = 0; + usleep_tv.tv_usec = 50000; + select(0, 0, 0, 0, &usleep_tv); + } while (timer.elapsed() < timeout); + + return 0; +} + +QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm) +{ + QByteArray buf; + QByteArray tmp_buf; + bool alloc_error = false; + int length; + int offset = 0; + + if (nbytes > 0) { + // Reserve buffer + zero-terminator (for text data) + // We want to complete the INCR transfer even if we cannot + // allocate more memory + buf.resize(nbytes+1); + alloc_error = buf.size() != nbytes+1; + } + + for (;;) { + connection()->flush(); + xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout); + if (!ge) + break; + + xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge; + if (event->atom != property || event->state != XCB_PROPERTY_NEW_VALUE) + continue; + if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) { + if (length == 0) { // no more data, we're done + if (nullterm) { + buf.resize(offset+1); + buf[offset] = '\0'; + } else { + buf.resize(offset); + } + return buf; + } else if (!alloc_error) { + if (offset+length > (int)buf.size()) { + buf.resize(offset+length+65535); + if (buf.size() != offset+length+65535) { + alloc_error = true; + length = buf.size() - offset; + } + } + memcpy(buf.data()+offset, tmp_buf.constData(), length); + tmp_buf.resize(0); + offset += length; + } + } else { + break; + } + + free(ge); + } + + // timed out ... create a new requestor window, otherwise the requestor + // could consider next request to be still part of this timed out request + setRequestor(0); + + return QByteArray(); +} + +QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom) +{ + return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::_QT_SELECTION)); +} + +QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property) +{ + QByteArray buf; + xcb_window_t win = requestor(); + + xcb_delete_property(xcb_connection(), win, property); + xcb_convert_selection(xcb_connection(), win, selection, target, property, connection()->time()); + + connection()->sync(); + + xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_SELECTION_NOTIFY, clipboard_timeout); + bool no_selection = !ge || ((xcb_selection_notify_event_t *)ge)->property == XCB_NONE; + free(ge); + + if (no_selection) + return buf; + + xcb_atom_t type; + if (clipboardReadProperty(win, property, true, &buf, 0, &type, 0)) { + if (type == atom(QXcbAtom::INCR)) { + int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; + buf = clipboardReadIncrementalProperty(win, property, nbytes, false); + } + } + + return buf; +} + +#include "qxcbclipboard.moc" diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h new file mode 100644 index 0000000000..d23f93529b --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbclipboard.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBCLIPBOARD_H +#define QXCBCLIPBOARD_H + +#include <QPlatformClipboard> +#include <qxcbobject.h> +#include <xcb/xcb.h> +#include <xcb/xfixes.h> + +class QXcbConnection; +class QXcbScreen; +class QXcbClipboardMime; + +class QXcbClipboard : public QXcbObject, public QPlatformClipboard +{ +public: + QXcbClipboard(QXcbConnection *connection); + ~QXcbClipboard(); + + QMimeData *mimeData(QClipboard::Mode mode); + void setMimeData(QMimeData *data, QClipboard::Mode mode); + + bool supportsMode(QClipboard::Mode mode) const; + bool ownsMode(QClipboard::Mode mode) const; + + QXcbScreen *screen() const { return m_screen; } + + xcb_window_t requestor() const; + void setRequestor(xcb_window_t window); + + xcb_window_t owner() const; + + void handleSelectionRequest(xcb_selection_request_event_t *event); + void handleSelectionClearRequest(xcb_selection_clear_event_t *event); + void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event); + + bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const; + QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm); + + QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom); + + xcb_window_t getSelectionOwner(xcb_atom_t atom) const; + QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property); + +private: + xcb_generic_event_t *waitForClipboardEvent(xcb_window_t win, int type, int timeout); + + xcb_atom_t sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property); + xcb_atom_t sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property); + + xcb_atom_t atomForMode(QClipboard::Mode mode) const; + QClipboard::Mode modeForAtom(xcb_atom_t atom) const; + + QXcbScreen *m_screen; + + // Selection and Clipboard + QXcbClipboardMime *m_xClipboard[2]; + QMimeData *m_clientClipboard[2]; + xcb_timestamp_t m_timestamp[2]; + + xcb_window_t m_requestor; + xcb_window_t m_owner; + + static const int clipboard_timeout; + +}; + +#endif // QXCBCLIPBOARD_H diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 80a1624380..a0b894602d 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -39,33 +39,41 @@ ** ****************************************************************************/ +#include <QtGui/private/qguiapplication_p.h> +#include <QtCore/QDebug> + #include "qxcbconnection.h" #include "qxcbkeyboard.h" #include "qxcbscreen.h" #include "qxcbwindow.h" +#include "qxcbclipboard.h" +#include "qxcbdrag.h" +#include "qxcbwmsupport.h" #include <QtAlgorithms> #include <QSocketNotifier> -#include <QtGui/private/qapplication_p.h> #include <QAbstractEventDispatcher> - -#include <QtCore/QDebug> +#include <QTimer> #include <stdio.h> #include <errno.h> +#include <xcb/xfixes.h> #ifdef XCB_USE_XLIB #include <X11/Xlib.h> #include <X11/Xlib-xcb.h> #endif +#ifdef XCB_USE_RENDER +#include <xcb/render.h> +#endif + #ifdef XCB_USE_EGL //dont pull in eglext prototypes #include <EGL/egl.h> #endif #ifdef XCB_USE_DRI2 #include <xcb/dri2.h> -#include <xcb/xfixes.h> extern "C" { #include <xf86drm.h> } @@ -83,12 +91,13 @@ QXcbConnection::QXcbConnection(const char *displayName) , m_dri2_support_probed(false) , m_has_support_for_dri2(false) #endif + , xfixes_first_event(0) { - int primaryScreen = 0; + m_primaryScreen = 0; #ifdef XCB_USE_XLIB Display *dpy = XOpenDisplay(m_displayName.constData()); - primaryScreen = DefaultScreen(dpy); + m_primaryScreen = DefaultScreen(dpy); m_connection = XGetXCBConnection(dpy); XSetEventQueueOwner(dpy, XCBOwnsEventQueue); m_xlib_display = dpy; @@ -100,13 +109,33 @@ QXcbConnection::QXcbConnection(const char *displayName) m_has_egl = eglInitialize(eglDisplay,&major,&minor); #endif //XCB_USE_EGL #else - m_connection = xcb_connect(m_displayName.constData(), &primaryScreen); - + m_connection = xcb_connect(m_displayName.constData(), &m_primaryScreen); #endif //XCB_USE_XLIB + + if (m_connection) + printf("Successfully connected to display %s\n", m_displayName.constData()); + + m_reader = new QXcbEventReader(m_connection); +#ifdef XCB_POLL_FOR_QUEUED_EVENT + connect(m_reader, SIGNAL(eventPending()), this, SLOT(processXcbEvents()), Qt::QueuedConnection); + m_reader->start(); +#else + QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcb_connection()), QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)), this, SLOT(processXcbEvents())); + + QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; + connect(dispatcher, SIGNAL(aboutToBlock()), this, SLOT(processXcbEvents())); + connect(dispatcher, SIGNAL(awake()), this, SLOT(processXcbEvents())); +#endif + + xcb_prefetch_extension_data (m_connection, &xcb_xfixes_id); + m_setup = xcb_get_setup(xcb_connection()); initializeAllAtoms(); + m_time = XCB_CURRENT_TIME; + xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int screenNumber = 0; @@ -115,25 +144,38 @@ QXcbConnection::QXcbConnection(const char *displayName) xcb_screen_next(&it); } + m_connectionEventListener = xcb_generate_id(m_connection); + xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, + m_connectionEventListener, m_screens.at(0)->root(), + 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, + m_screens.at(0)->screen()->root_visual, 0, 0); + + initializeXFixes(); + initializeXRender(); + + m_wmSupport = new QXcbWMSupport(this); m_keyboard = new QXcbKeyboard(this); + m_clipboard = new QXcbClipboard(this); + m_drag = new QXcbDrag(this); #ifdef XCB_USE_DRI2 initializeDri2(); #endif - - QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcb_connection()), QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)), this, SLOT(processXcbEvents())); - - QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(qApp->thread()); - connect(dispatcher, SIGNAL(aboutToBlock()), this, SLOT(processXcbEvents())); - sync(); } QXcbConnection::~QXcbConnection() { + delete m_clipboard; + qDeleteAll(m_screens); +#ifdef XCB_POLL_FOR_QUEUED_EVENT + sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION); + m_reader->wait(); +#endif + delete m_reader; + #ifdef XCB_USE_XLIB XCloseDisplay((Display *)m_xlib_display); #else @@ -143,22 +185,26 @@ QXcbConnection::~QXcbConnection() delete m_keyboard; } -QXcbWindow *platformWindowFromId(xcb_window_t id) +void QXcbConnection::addWindow(xcb_window_t id, QXcbWindow *window) { - QWidget *widget = QWidget::find(id); - if (widget) - return static_cast<QXcbWindow *>(widget->platformWindow()); - return 0; + m_mapper.insert(id, window); +} + +void QXcbConnection::removeWindow(xcb_window_t id) +{ + m_mapper.remove(id); +} + +QXcbWindow *QXcbConnection::platformWindowFromId(xcb_window_t id) +{ + return m_mapper.value(id, 0); } -#define HANDLE_PLATFORM_WINDOW_EVENT(event_t, window, handler) \ +#define HANDLE_PLATFORM_WINDOW_EVENT(event_t, windowMember, handler) \ { \ event_t *e = (event_t *)event; \ - if (QXcbWindow *platformWindow = platformWindowFromId(e->window)) { \ - QObjectPrivate *d = QObjectPrivate::get(platformWindow->widget()); \ - if (!d->wasDeleted) \ - platformWindow->handler(e); \ - } \ + if (QXcbWindow *platformWindow = platformWindowFromId(e->windowMember)) \ + platformWindow->handler(e); \ } \ break; @@ -166,7 +212,7 @@ break; { \ event_t *e = (event_t *)event; \ if (QXcbWindow *platformWindow = platformWindowFromId(e->event)) \ - m_keyboard->handler(platformWindow->widget(), e); \ + m_keyboard->handler(platformWindow, e); \ } \ break; @@ -373,6 +419,7 @@ const char *xcb_protocol_request_codes[] = #ifdef Q_XCB_DEBUG void QXcbConnection::log(const char *file, int line, int sequence) { + QMutexLocker locker(&m_callLogMutex); CallInfo info; info.sequence = sequence; info.file = file; @@ -392,6 +439,7 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error) int(error->major_code), xcb_protocol_request_codes[clamped_major_code], int(error->minor_code)); #ifdef Q_XCB_DEBUG + QMutexLocker locker(&m_callLogMutex); int i = 0; for (; i < m_callLog.size(); ++i) { if (m_callLog.at(i).sequence == error->sequence) { @@ -409,67 +457,236 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error) #endif } +void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) +{ +#ifdef Q_XCB_DEBUG + { + QMutexLocker locker(&m_callLogMutex); + int i = 0; + for (; i < m_callLog.size(); ++i) + if (m_callLog.at(i).sequence >= event->sequence) + break; + m_callLog.remove(0, i); + } +#endif + bool handled = true; + + uint response_type = event->response_type & ~0x80; + + switch (response_type) { + case XCB_EXPOSE: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent); + case XCB_BUTTON_PRESS: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent); + case XCB_BUTTON_RELEASE: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); + case XCB_MOTION_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent); + case XCB_CONFIGURE_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent); + case XCB_MAP_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent); + case XCB_UNMAP_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent); + case XCB_CLIENT_MESSAGE: + handleClientMessageEvent((xcb_client_message_event_t *)event); + case XCB_ENTER_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); + case XCB_LEAVE_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent); + case XCB_FOCUS_IN: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent); + case XCB_FOCUS_OUT: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); + case XCB_KEY_PRESS: + HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); + case XCB_KEY_RELEASE: + HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); + case XCB_MAPPING_NOTIFY: + m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); + break; + case XCB_SELECTION_REQUEST: + { + xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event; + if (sr->selection == atom(QXcbAtom::XdndSelection)) + m_drag->handleSelectionRequest(sr); + else + m_clipboard->handleSelectionRequest(sr); + break; + } + case XCB_SELECTION_CLEAR: + setTime(((xcb_selection_clear_event_t *)event)->time); + m_clipboard->handleSelectionClearRequest((xcb_selection_clear_event_t *)event); + handled = true; + break; + case XCB_SELECTION_NOTIFY: + setTime(((xcb_selection_notify_event_t *)event)->time); + qDebug() << "XCB_SELECTION_NOTIFY"; + handled = false; + break; + case XCB_PROPERTY_NOTIFY: + setTime(((xcb_property_notify_event_t *)event)->time); +// qDebug() << "XCB_PROPERTY_NOTIFY"; + handled = false; + break; + default: + handled = false; + break; + } + + if (!handled) { + if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { + setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp); + m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event); + handled = true; + } + } + + if (handled) + printXcbEvent("Handled XCB event", event); + else + printXcbEvent("Unhandled XCB event", event); +} + +void QXcbConnection::addPeekFunc(PeekFunc f) +{ + m_peekFuncs.append(f); +} + +#ifdef XCB_POLL_FOR_QUEUED_EVENT +void QXcbEventReader::run() +{ + xcb_generic_event_t *event; + while (m_connection && (event = xcb_wait_for_event(m_connection))) { + m_mutex.lock(); + addEvent(event); + while (m_connection && (event = xcb_poll_for_queued_event(m_connection))) + addEvent(event); + m_mutex.unlock(); + emit eventPending(); + } + + for (int i = 0; i < m_events.size(); ++i) + free(m_events.at(i)); +} +#endif + +void QXcbEventReader::addEvent(xcb_generic_event_t *event) +{ + if ((event->response_type & ~0x80) == XCB_CLIENT_MESSAGE + && ((xcb_client_message_event_t *)event)->type == QXcbAtom::_QT_CLOSE_CONNECTION) + m_connection = 0; + m_events << event; +} + +QList<xcb_generic_event_t *> *QXcbEventReader::lock() +{ + m_mutex.lock(); +#ifndef XCB_POLL_FOR_QUEUED_EVENT + while (xcb_generic_event_t *event = xcb_poll_for_event(m_connection)) + m_events << event; +#endif + return &m_events; +} + +void QXcbEventReader::unlock() +{ + m_mutex.unlock(); +} + +void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom atom, uint id) +{ + xcb_client_message_event_t event; + memset(&event, 0, sizeof(event)); + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.sequence = 0; + event.window = m_connectionEventListener; + event.type = atom; + event.data.data32[0] = id; + + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event)); + xcb_flush(xcb_connection()); +} + void QXcbConnection::processXcbEvents() { - while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection())) { - bool handled = true; + QList<xcb_generic_event_t *> *eventqueue = m_reader->lock(); + + for(int i = 0; i < eventqueue->size(); ++i) { + xcb_generic_event_t *event = eventqueue->at(i); + if (!event) + continue; + (*eventqueue)[i] = 0; uint response_type = event->response_type & ~0x80; if (!response_type) { handleXcbError((xcb_generic_error_t *)event); - continue; + } else { + 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)(event)) + it = m_peekFuncs.erase(it); + else + ++it; + } + handleXcbEvent(event); } -#ifdef Q_XCB_DEBUG - { - int i = 0; - for (; i < m_callLog.size(); ++i) - if (m_callLog.at(i).sequence >= event->sequence) - break; - m_callLog.remove(0, i); - } -#endif + free(event); + } - switch (response_type) { - case XCB_EXPOSE: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent); - case XCB_BUTTON_PRESS: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent); - case XCB_BUTTON_RELEASE: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); - case XCB_MOTION_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent); - case XCB_CONFIGURE_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent); - case XCB_CLIENT_MESSAGE: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent); - case XCB_ENTER_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); - case XCB_LEAVE_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent); - case XCB_FOCUS_IN: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent); - case XCB_FOCUS_OUT: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); - case XCB_KEY_PRESS: - HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); - case XCB_KEY_RELEASE: - HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); - case XCB_MAPPING_NOTIFY: - m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); - break; - default: - handled = false; - break; + eventqueue->clear(); + + m_reader->unlock(); + + // Indicate with a null event that the event the callbacks are waiting for + // is not in the queue currently. + Q_FOREACH (PeekFunc f, m_peekFuncs) + f(0); + m_peekFuncs.clear(); + + xcb_flush(xcb_connection()); +} + +void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *event) +{ + if (event->format != 32) + return; + + if (event->type == atom(QXcbAtom::XdndStatus)) { + drag()->handleStatus(event, false); + } else if (event->type == atom(QXcbAtom::XdndFinished)) { + drag()->handleFinished(event, false); + } + + QXcbWindow *window = platformWindowFromId(event->window); + if (!window) + return; + + window->handleClientMessageEvent(event); +} + +xcb_generic_event_t *QXcbConnection::checkEvent(int type) +{ + QList<xcb_generic_event_t *> *eventqueue = m_reader->lock(); + + for (int i = 0; i < eventqueue->size(); ++i) { + xcb_generic_event_t *event = eventqueue->at(i); + if (event->response_type == type) { + (*eventqueue)[i] = 0; + m_reader->unlock(); + return event; } - if (handled) - printXcbEvent("Handled XCB event", event); - else - printXcbEvent("Unhandled XCB event", event); } - xcb_flush(xcb_connection()); + m_reader->unlock(); + + return 0; } static const char * xcb_atomnames = { @@ -511,6 +728,8 @@ static const char * xcb_atomnames = { "_QT_SCROLL_DONE\0" "_QT_INPUT_ENCODING\0" + "_QT_CLOSE_CONNECTION\0" + "_MOTIF_WM_HINTS\0" "DTWM_IS_RUNNING\0" @@ -579,7 +798,6 @@ static const char * xcb_atomnames = { "_NET_ACTIVE_WINDOW\0" // Property formats - "COMPOUND_TEXT\0" "TEXT\0" "UTF8_STRING\0" @@ -659,8 +877,57 @@ void QXcbConnection::initializeAllAtoms() { for (i = 0; i < QXcbAtom::NAtoms; ++i) cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); - for (i = 0; i < QXcbAtom::NAtoms; ++i) - m_allAtoms[i] = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0)->atom; + for (i = 0; i < QXcbAtom::NAtoms; ++i) { + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); + m_allAtoms[i] = reply->atom; + free(reply); + } +} + +xcb_atom_t QXcbConnection::internAtom(const char *name) +{ + if (!name || *name == 0) + return XCB_NONE; + + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0); + int atom = reply->atom; + free(reply); + return atom; +} + +QByteArray QXcbConnection::atomName(xcb_atom_t atom) +{ + if (!atom) + return QByteArray(); + + xcb_generic_error_t *error = 0; + xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom)); + xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error); + if (error) { + qWarning() << "QXcbConnection::atomName: bad Atom" << atom; + } + if (reply) { + QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); + free(reply); + return result; + } + return QByteArray(); +} + +const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const +{ + xcb_format_iterator_t iterator = + xcb_setup_pixmap_formats_iterator(m_setup); + + while (iterator.rem) { + xcb_format_t *format = iterator.data; + if (format->depth == depth) + return format; + xcb_format_next(&iterator); + } + + return 0; } void QXcbConnection::sync() @@ -670,6 +937,43 @@ void QXcbConnection::sync() free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0)); } +void QXcbConnection::initializeXFixes() +{ + xcb_generic_error_t *error = 0; + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id); + xfixes_first_event = reply->first_event; + + xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection, + xfixes_query_cookie, &error); + if (!xfixes_query || error || xfixes_query->major_version < 2) { + qWarning("Failed to initialize XFixes"); + free(error); + xfixes_first_event = 0; + } + free(xfixes_query); + +} + +void QXcbConnection::initializeXRender() +{ +#ifdef XCB_USE_RENDER + xcb_generic_error_t *error = 0; + xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection, + XCB_RENDER_MAJOR_VERSION, + XCB_RENDER_MINOR_VERSION); + xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection, + xrender_query_cookie, &error); + if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) { + qWarning("Failed to initialize XRender"); + free(error); + } + free(xrender_query); +#endif +} + #if defined(XCB_USE_EGL) bool QXcbConnection::hasEgl() const { @@ -740,26 +1044,12 @@ bool QXcbConnection::hasSupportForDri2() const if (!m_dri2_support_probed) { xcb_generic_error_t *error = 0; - xcb_prefetch_extension_data (m_connection, &xcb_xfixes_id); xcb_prefetch_extension_data (m_connection, &xcb_dri2_id); - xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection, - XCB_XFIXES_MAJOR_VERSION, - XCB_XFIXES_MINOR_VERSION); - xcb_dri2_query_version_cookie_t dri2_query_cookie = xcb_dri2_query_version (m_connection, XCB_DRI2_MAJOR_VERSION, XCB_DRI2_MINOR_VERSION); - xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection, - xfixes_query_cookie, &error); - if (!xfixes_query || error || xfixes_query->major_version < 2) { - delete error; - delete xfixes_query; - return false; - } - delete xfixes_query; - xcb_dri2_query_version_reply_t *dri2_query = xcb_dri2_query_version_reply (m_connection, dri2_query_cookie, &error); if (!dri2_query || error) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 01bc719546..6d93093072 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -44,13 +44,23 @@ #include <xcb/xcb.h> +#include <QHash> #include <QList> +#include <QMutex> #include <QObject> +#include <QThread> #include <QVector> #define Q_XCB_DEBUG class QXcbScreen; +class QXcbWindow; +class QXcbDrag; +class QXcbKeyboard; +class QXcbClipboard; +class QXcbWMSupport; + +typedef QHash<xcb_window_t, QXcbWindow *> WindowMapper; namespace QXcbAtom { enum Atom { @@ -92,6 +102,9 @@ namespace QXcbAtom { _QT_SCROLL_DONE, _QT_INPUT_ENCODING, + // Qt/XCB specific + _QT_CLOSE_CONNECTION, + _MOTIF_WM_HINTS, DTWM_IS_RUNNING, @@ -160,7 +173,6 @@ namespace QXcbAtom { _NET_ACTIVE_WINDOW, // Property formats - COMPOUND_TEXT, TEXT, UTF8_STRING, @@ -215,8 +227,34 @@ namespace QXcbAtom { }; } -class QXcbKeyboard; +class QXcbEventReader : public QThread +{ + Q_OBJECT +public: + QXcbEventReader(xcb_connection_t *connection) + : m_connection(connection) + { + } + +#ifdef XCB_POLL_FOR_QUEUED_EVENT + void run(); +#endif + QList<xcb_generic_event_t *> *lock(); + void unlock(); + +signals: + void eventPending(); + +private: + void addEvent(xcb_generic_event_t *event); + + QMutex m_mutex; + QList<xcb_generic_event_t *> m_events; + xcb_connection_t *m_connection; +}; + +class QAbstractEventDispatcher; class QXcbConnection : public QObject { Q_OBJECT @@ -226,17 +264,26 @@ public: QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } - QList<QXcbScreen *> screens() const { return m_screens; } + const QList<QXcbScreen *> &screens() const { return m_screens; } int primaryScreen() const { return m_primaryScreen; } xcb_atom_t atom(QXcbAtom::Atom atom); + xcb_atom_t internAtom(const char *name); + QByteArray atomName(xcb_atom_t atom); const char *displayName() const { return m_displayName.constData(); } xcb_connection_t *xcb_connection() const { return m_connection; } + const xcb_setup_t *setup() const { return m_setup; } + const xcb_format_t *formatForDepth(uint8_t depth) const; QXcbKeyboard *keyboard() const { return m_keyboard; } + QXcbClipboard *clipboard() const { return m_clipboard; } + QXcbDrag *drag() const { return m_drag; } + + QXcbWMSupport *wmSupport() const { return m_wmSupport; } + #ifdef XCB_USE_XLIB void *xlib_display() const { return m_xlib_display; } #endif @@ -253,7 +300,26 @@ public: #endif void sync(); + void flush() { xcb_flush(m_connection); } + void handleXcbError(xcb_generic_error_t *error); + void handleXcbEvent(xcb_generic_event_t *event); + + void addWindow(xcb_window_t id, QXcbWindow *window); + void removeWindow(xcb_window_t id); + QXcbWindow *platformWindowFromId(xcb_window_t id); + + xcb_generic_event_t *checkEvent(int type); + template<typename T> + inline xcb_generic_event_t *checkEvent(const T &checker); + + typedef bool (*PeekFunc)(xcb_generic_event_t *); + void addPeekFunc(PeekFunc f); + + inline xcb_timestamp_t time() const { return m_time; } + inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; } + + bool hasXFixes() const { return xfixes_first_event > 0; } private slots: void processXcbEvents(); @@ -261,9 +327,12 @@ private slots: private: void initializeAllAtoms(); void sendConnectionEvent(QXcbAtom::Atom atom, uint id = 0); + void initializeXFixes(); + void initializeXRender(); #ifdef XCB_USE_DRI2 void initializeDri2(); #endif + void handleClientMessageEvent(const xcb_client_message_event_t *event); xcb_connection_t *m_connection; const xcb_setup_t *m_setup; @@ -273,14 +342,21 @@ private: xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; + xcb_timestamp_t m_time; + QByteArray m_displayName; + xcb_window_t m_connectionEventListener; + QXcbKeyboard *m_keyboard; + QXcbClipboard *m_clipboard; + QXcbDrag *m_drag; + QXcbWMSupport *m_wmSupport; #if defined(XCB_USE_XLIB) void *m_xlib_display; #endif - + QXcbEventReader *m_reader; #ifdef XCB_USE_DRI2 uint32_t m_dri2_major; uint32_t m_dri2_minor; @@ -299,14 +375,39 @@ private: int line; }; QVector<CallInfo> m_callLog; + QMutex m_callLogMutex; void log(const char *file, int line, int sequence); template <typename cookie_t> friend cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file, int line); #endif + + WindowMapper m_mapper; + + QVector<PeekFunc> m_peekFuncs; + + uint32_t xfixes_first_event; }; #define DISPLAY_FROM_XCB(object) ((Display *)(object->connection()->xlib_display())) +template<typename T> +xcb_generic_event_t *QXcbConnection::checkEvent(const T &checker) +{ + QList<xcb_generic_event_t *> *eventqueue = m_reader->lock(); + + for (int i = 0; i < eventqueue->size(); ++i) { + xcb_generic_event_t *event = eventqueue->at(i); + if (checker.check(event)) { + (*eventqueue)[i] = 0; + m_reader->unlock(); + return event; + } + } + m_reader->unlock(); + return 0; +} + + #ifdef Q_XCB_DEBUG template <typename cookie_t> cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file, int line) diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp new file mode 100644 index 0000000000..f6856d5694 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -0,0 +1,546 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbcursor.h" +#include "qxcbconnection.h" +#include "qxcbwindow.h" +#include "qxcbimage.h" +#include <QtCore/QLibrary> +#include <QtGui/QWindow> +#include <QtGui/QBitmap> +#include <QtGui/private/qguiapplication_p.h> +#include <X11/cursorfont.h> +#include <xcb/xfixes.h> +#include <xcb/xcb_image.h> + +QT_BEGIN_NAMESPACE + +typedef int (*PtrXcursorLibraryLoadCursor)(void *, const char *); +static PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor = 0; +static xcb_font_t cursorFont = 0; +static int cursorCount = 0; + +static uint8_t cur_blank_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uint8_t cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 }; +static const uint8_t mcur_ver_bits[] = { + 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, + 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 }; +static const uint8_t cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18, + 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c, + 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c, + 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 }; +static const uint8_t cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, + 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t mcur_bdiag_bits[] = { + 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f, + 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, + 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, + 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, + 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e, + 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 }; +static const uint8_t *cursor_bits16[] = { + cur_ver_bits, mcur_ver_bits, cur_hor_bits, mcur_hor_bits, + cur_bdiag_bits, mcur_bdiag_bits, cur_fdiag_bits, mcur_fdiag_bits, + 0, 0, cur_blank_bits, cur_blank_bits }; + +static const uint8_t vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t whatsthis_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00, + 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00, + 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00, + 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00, + 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uint8_t whatsthism_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00, + 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00, + 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00, + 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00, + 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00, + 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uint8_t busy_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00, + 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00, + 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00, + 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00, + 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uint8_t busym_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00, + 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00, + 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00, + 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00, + 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const uint8_t * const cursor_bits32[] = { + vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits, + 0, 0, 0, 0, whatsthis_bits, whatsthism_bits, busy_bits, busym_bits +}; + +static const uint8_t forbidden_bits[] = { + 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01, + 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06, + 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03, + 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 }; + +static const uint8_t forbiddenm_bits[] = { + 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03, + 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f, + 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07, + 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00}; + +static const uint8_t openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; +static const uint8_t openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; +static const uint8_t closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; +static const uint8_t closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + +static const uint8_t * const cursor_bits20[] = { + forbidden_bits, forbiddenm_bits +}; + +static const char * const cursorNames[] = { + "left_ptr", + "up_arrow", + "cross", + "wait", + "ibeam", + "size_ver", + "size_hor", + "size_bdiag", + "size_fdiag", + "size_all", + "blank", + "split_v", + "split_h", + "pointing_hand", + "forbidden", + "whats_this", + "left_ptr_watch", + "openhand", + "closedhand", + "copy", + "move", + "link" +}; + +QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) + : QXcbObject(conn), QPlatformCursor(screen), m_screen(screen) +{ + if (cursorCount++) + return; + + cursorFont = xcb_generate_id(xcb_connection()); + const char *cursorStr = "cursor"; + xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); + +#ifdef XCB_USE_XLIB + QLibrary xcursorLib(QLatin1String("Xcursor"), 1); + bool xcursorFound = xcursorLib.load(); + if (!xcursorFound) { // try without the version number + xcursorLib.setFileName(QLatin1String("Xcursor")); + xcursorFound = xcursorLib.load(); + } + if (xcursorFound) + ptrXcursorLibraryLoadCursor = + (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); +#endif +} + +QXcbCursor::~QXcbCursor() +{ + if (!--cursorCount) + xcb_close_font(xcb_connection(), cursorFont); +} + +void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget) +{ + QXcbWindow *w = 0; + if (widget && widget->handle()) + w = static_cast<QXcbWindow *>(widget->handle()); + else + // No X11 cursor control when there is no widget under the cursor + return; + + xcb_cursor_t c; + if (cursor->shape() == Qt::BitmapCursor) { + qint64 id = cursor->pixmap().cacheKey(); + if (!m_bitmapCursorMap.contains(id)) + m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); + c = m_bitmapCursorMap.value(id); + } else { + int id = cursor->handle(); + if (!m_shapeCursorMap.contains(id)) + m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); + c = m_shapeCursorMap.value(id); + } + + w->setCursor(c); +} + +static int cursorIdForShape(int cshape) +{ + int cursorId = 0; + switch (cshape) { + case Qt::ArrowCursor: + cursorId = XC_left_ptr; + break; + case Qt::UpArrowCursor: + cursorId = XC_center_ptr; + break; + case Qt::CrossCursor: + cursorId = XC_crosshair; + break; + case Qt::WaitCursor: + cursorId = XC_watch; + break; + case Qt::IBeamCursor: + cursorId = XC_xterm; + break; + case Qt::SizeAllCursor: + cursorId = XC_fleur; + break; + case Qt::PointingHandCursor: + cursorId = XC_hand2; + break; + case Qt::SizeBDiagCursor: + cursorId = XC_top_right_corner; + break; + case Qt::SizeFDiagCursor: + cursorId = XC_bottom_right_corner; + break; + case Qt::SizeVerCursor: + case Qt::SplitVCursor: + cursorId = XC_sb_v_double_arrow; + break; + case Qt::SizeHorCursor: + case Qt::SplitHCursor: + cursorId = XC_sb_h_double_arrow; + break; + case Qt::WhatsThisCursor: + cursorId = XC_question_arrow; + break; + case Qt::ForbiddenCursor: + cursorId = XC_circle; + break; + case Qt::BusyCursor: + cursorId = XC_watch; + break; + default: + break; + } + return cursorId; +} + +xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) +{ + xcb_cursor_t cursor = 0; + xcb_connection_t *conn = xcb_connection(); + + if (cshape == Qt::BlankCursor) { + xcb_pixmap_t cp = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), cur_blank_bits, 16, 16, + 1, 0, 0, 0); + xcb_pixmap_t mp = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), cur_blank_bits, 16, 16, + 1, 0, 0, 0); + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 8, 8); + } else if (cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor) { + int i = (cshape - Qt::SizeVerCursor) * 2; + xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits16[i]), + 16, 16, 1, 0, 0, 0); + xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits16[i + 1]), + 16, 16, 1, 0, 0, 0); + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, pm, pmm, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 8, 8); + } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor) + || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) { + int i = (cshape - Qt::SplitVCursor) * 2; + xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits32[i]), + 32, 32, 1, 0, 0, 0); + xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits32[i + 1]), + 32, 32, 1, 0, 0, 0); + int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor + || cshape == Qt::BusyCursor) ? 0 : 16; + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, pm, pmm, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, hs, hs); + } else if (cshape == Qt::ForbiddenCursor) { + int i = (cshape - Qt::ForbiddenCursor) * 2; + xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits20[i]), + 20, 20, 1, 0, 0, 0); + xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(cursor_bits20[i + 1]), + 20, 20, 1, 0, 0, 0); + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, pm, pmm, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 10, 10); + } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) { + bool open = cshape == Qt::OpenHandCursor; + xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(open ? openhand_bits : closedhand_bits), + 16, 16, 1, 0, 0, 0); + xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(conn, m_screen->root(), + const_cast<uint8_t*>(open ? openhandm_bits : closedhandm_bits), + 16, 16, 1, 0, 0, 0); + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, pm, pmm, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 8, 8); + } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor + || cshape == Qt::DragLinkCursor) { + QImage image = QGuiApplicationPrivate::instance()->getPixmapCursor(static_cast<Qt::CursorShape>(cshape)).toImage(); + xcb_pixmap_t pm = qt_xcb_XPixmapFromBitmap(m_screen, image); + xcb_pixmap_t pmm = qt_xcb_XPixmapFromBitmap(m_screen, image.createAlphaMask()); + cursor = xcb_generate_id(conn); + xcb_create_cursor(conn, cursor, pm, pmm, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 8, 8); + } + + return cursor; +} + +xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +{ + xcb_connection_t *conn = xcb_connection(); + int cursorId = cursorIdForShape(cshape); + xcb_cursor_t cursor = XCB_NONE; + + // Try Xcursor first +#ifdef XCB_USE_XLIB + if (ptrXcursorLibraryLoadCursor && cshape >= 0 && cshape < Qt::LastCursor) { + void *dpy = connection()->xlib_display(); + // special case for non-standard dnd-* cursors + switch (cshape) { + case Qt::DragCopyCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); + break; + case Qt::DragMoveCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); + break; + case Qt::DragLinkCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); + break; + default: + break; + } + if (!cursor) + cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + } + if (cursor) + return cursor; +#endif + + // Non-standard X11 cursors are created from bitmaps + cursor = createNonStandardCursor(cshape); + + // Create a glpyh cursor if everything else failed + if (!cursor && cursorId) { + cursor = xcb_generate_id(conn); + xcb_create_glyph_cursor(conn, cursor, cursorFont, cursorFont, + cursorId, cursorId + 1, + 0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0); + } + + if (cursor && cshape >= 0 && cshape < Qt::LastCursor) { + const char *name = cursorNames[cshape]; + xcb_xfixes_set_cursor_name(conn, cursor, strlen(name), name); + } + + return cursor; +} + +xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) +{ + xcb_connection_t *conn = xcb_connection(); + QPoint spot = cursor->hotSpot(); + xcb_cursor_t c = XCB_NONE; + if (cursor->pixmap().depth() > 1) + c = qt_xcb_createCursorXRender(m_screen, cursor->pixmap().toImage(), spot); + if (!c) { + xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage()); + xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage()); + c = xcb_generate_id(conn); + xcb_create_cursor(conn, c, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, + spot.x(), spot.y()); + xcb_free_pixmap(conn, cp); + xcb_free_pixmap(conn, mp); + } + return c; +} + +static void getPosAndRoot(xcb_connection_t *conn, xcb_window_t *rootWin, QPoint *pos) +{ + if (pos) + *pos = QPoint(); + xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(conn)); + while (it.rem) { + xcb_window_t root = it.data->root; + xcb_query_pointer_cookie_t cookie = xcb_query_pointer(conn, root); + xcb_generic_error_t *err = 0; + xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(conn, cookie, &err); + if (!err && reply) { + if (pos) + *pos = QPoint(reply->root_x, reply->root_y); + if (rootWin) + *rootWin = root; + free(reply); + return; + } + free(err); + free(reply); + xcb_screen_next(&it); + } +} + +QPoint QXcbCursor::pos() const +{ + QPoint p; + getPosAndRoot(xcb_connection(), 0, &p); + return p; +} + +void QXcbCursor::setPos(const QPoint &pos) +{ + xcb_connection_t *conn = xcb_connection(); + xcb_window_t root; + getPosAndRoot(conn, &root, 0); + xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, pos.x(), pos.y()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h new file mode 100644 index 0000000000..4bbb9a928b --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBCURSOR_H +#define QXCBCURSOR_H + +#include <QtGui/QPlatformCursor> +#include "qxcbscreen.h" + +QT_BEGIN_NAMESPACE + +class QXcbCursor : public QXcbObject, public QPlatformCursor +{ +public: + QXcbCursor(QXcbConnection *conn, QXcbScreen *screen); + ~QXcbCursor(); + void changeCursor(QCursor *cursor, QWindow *widget); + QPoint pos() const; + void setPos(const QPoint &pos); + +private: + xcb_cursor_t createFontCursor(int cshape); + xcb_cursor_t createBitmapCursor(QCursor *cursor); + xcb_cursor_t createNonStandardCursor(int cshape); + + QXcbScreen *m_screen; + QMap<int, xcb_cursor_t> m_shapeCursorMap; + QMap<qint64, xcb_cursor_t> m_bitmapCursorMap; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp new file mode 100644 index 0000000000..8a6fd29986 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -0,0 +1,1332 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbdrag.h" +#include <xcb/xcb.h> +#include "qxcbconnection.h" +#include "qxcbclipboard.h" +#include "qxcbmime.h" +#include "qxcbwindow.h" +#include "qxcbscreen.h" +#include "qwindow.h" +#include <private/qdnd_p.h> +#include <qdebug.h> +#include <qevent.h> +#include <qguiapplication.h> +#include <qrect.h> + +QT_BEGIN_NAMESPACE + +//#define DND_DEBUG +#ifdef DND_DEBUG +#define DEBUG qDebug +#else +#define DEBUG if(0) qDebug +#endif + +#ifdef DND_DEBUG +#define DNDDEBUG qDebug() +#else +#define DNDDEBUG if(0) qDebug() +#endif + +const int xdnd_version = 5; + +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; + + xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy), + XCB_ATOM_WINDOW, 0, 1), c); + xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); + + if (reply && reply->type == XCB_ATOM_WINDOW) + proxy = *((xcb_window_t *)xcb_get_property_value(reply)); + free(reply); + + if (proxy == XCB_NONE) + return proxy; + + // exists and is real? + cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy), + XCB_ATOM_WINDOW, 0, 1), c); + reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); + + if (reply && reply->type == XCB_ATOM_WINDOW) { + xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply)); + if (proxy != p) + proxy = 0; + } else { + proxy = 0; + } + + free(reply); + + return proxy; +} + + +class QDropData : public QXcbMime +{ +public: + QDropData(QXcbDrag *d); + ~QDropData(); + +protected: + bool hasFormat_sys(const QString &mimeType) const; + QStringList formats_sys() const; + QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const; + + QVariant xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const; + + QXcbDrag *drag; +}; + + +QXcbDrag::QXcbDrag(QXcbConnection *c) + : QXcbObject(c) +{ + dropData = new QDropData(this); + + init(); + heartbeat = -1; + + transaction_expiry_timer = -1; +} + +QXcbDrag::~QXcbDrag() +{ + delete dropData; +} + +void QXcbDrag::init() +{ + currentWindow.clear(); + + xdnd_dragsource = XCB_NONE; + last_target_accepted_action = Qt::IgnoreAction; + + waiting_for_status = false; + current_target = XCB_NONE; + current_proxy_target = XCB_NONE; + xdnd_dragging = false; + + source_time = XCB_CURRENT_TIME; + target_time = XCB_CURRENT_TIME; + + current_screen = 0; + drag_types.clear(); +} + +QMimeData *QXcbDrag::platformDropData() +{ + return dropData; +} + +void QXcbDrag::startDrag() +{ + init(); + + heartbeat = startTimer(200); + xdnd_dragging = true; + + xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(), + atom(QXcbAtom::XdndSelection), connection()->time()); + + QDragManager *manager = QDragManager::self(); + QStringList fmts = QXcbMime::formatsHelper(manager->dropData()); + for (int i = 0; i < fmts.size(); ++i) { + QList<xcb_atom_t> atoms = QXcbMime::mimeAtomsForFormat(connection(), fmts.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!drag_types.contains(atoms.at(j))) + drag_types.append(atoms.at(j)); + } + } + if (drag_types.size() > 3) + 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()); + + QPointF pos = QCursor::pos(); + QMouseEvent me(QEvent::MouseMove, pos, pos, pos, Qt::LeftButton, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + move(&me); + +// if (!QWidget::mouseGrabber()) +// manager->shapedPixmapWindow->grabMouse(); +} + +void QXcbDrag::endDrag() +{ + Q_ASSERT(heartbeat != -1); + killTimer(heartbeat); + heartbeat = -1; + + xdnd_dragging = false; +} + +static xcb_translate_coordinates_reply_t * +translateCoordinates(QXcbConnection *c, xcb_window_t from, xcb_window_t to, int x, int y) +{ + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(c->xcb_connection(), from, to, x, y); + return xcb_translate_coordinates_reply(c->xcb_connection(), cookie, 0); +} + +void QXcbDrag::move(const QMouseEvent *me) +{ + DEBUG() << "QDragManager::move enter"; + + // ### + QPoint globalPos = me->globalPos(); + + if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) + return; + + const QList<QXcbScreen *> &screens = connection()->screens(); + QXcbScreen *screen = screens.at(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; + } + + +// qt_xdnd_current_screen = screen; + xcb_window_t rootwin = current_screen->root(); + xcb_translate_coordinates_reply_t *translate = + ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); + if (!translate) + return; + xcb_window_t target = translate->child; + int lx = translate->dst_x; + int ly = translate->dst_y; + free (translate); + + if (target == rootwin) { + // Ok. + } else if (target) { + //me + xcb_window_t src = rootwin; + while (target != 0) { + DNDDEBUG << "checking target for XdndAware" << target; + + // translate coordinates + translate = ::translateCoordinates(connection(), src, target, lx, ly); + if (!translate) { + target = 0; + break; + } + lx = translate->dst_x; + ly = translate->dst_y; + src = translate->child; + free(translate); + + // check if it has XdndAware + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, target, + atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0)); + xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0); + bool aware = reply && reply->type != XCB_NONE; + free(reply); + if (aware) { + DNDDEBUG << "Found XdndAware on " << target; + break; + } + + // find child at the coordinates + translate = ::translateCoordinates(connection(), src, src, lx, ly); + if (!translate) { + target = 0; + break; + } + target = translate->child; + free(translate); + } + // #### +// if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) { +// DNDDEBUG << "need to find real window"; +// target = findRealWindow(globalPos, rootwin, 6); +// DNDDEBUG << "real window found" << QWidget::find(target) << target; +// } + } + + QXcbWindow *w = 0; + if (target) { + w = connection()->platformWindowFromId(target); + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) + w = 0; + } else { + w = 0; + target = rootwin; + } + + DNDDEBUG << "and the final target is " << target; + DNDDEBUG << "the widget w is" << (w ? w->window() : 0); + + xcb_window_t proxy_target = xdndProxy(connection(), target); + if (!proxy_target) + proxy_target = target; + int target_version = 1; + + if (proxy_target) { + xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, target, + atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1); + xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0); + if (!reply || reply->type == XCB_NONE) + target = 0; + target_version = xcb_get_property_value_length(reply) == 1 ? *(uint32_t *)xcb_get_property_value(reply) : 1; + if (target_version > xdnd_version) + target_version = xdnd_version; + + free(reply); + } + + DEBUG() << "target=" << target << "current_target=" << current_target; + if (target != current_target) { + if (current_target) + send_leave(); + + current_target = target; + current_proxy_target = proxy_target; + if (target) { + int flags = target_version << 24; + if (drag_types.size() > 3) + flags |= 0x0001; + + xcb_client_message_event_t enter; + enter.response_type = XCB_CLIENT_MESSAGE; + enter.window = target; + enter.format = 32; + enter.type = atom(QXcbAtom::XdndEnter); + enter.data.data32[0] = connection()->clipboard()->owner(); + enter.data.data32[1] = flags; + enter.data.data32[2] = drag_types.size()>0 ? drag_types.at(0) : 0; + enter.data.data32[3] = drag_types.size()>1 ? drag_types.at(1) : 0; + enter.data.data32[4] = drag_types.size()>2 ? drag_types.at(2) : 0; + // provisionally set the rectangle to 5x5 pixels... + source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() -2 , 5, 5); + + DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0]; + if (w) + handleEnter(w->window(), &enter); + else if (target) + xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter); + waiting_for_status = false; + } + } + if (waiting_for_status) + return; + + QDragManager *m = QDragManager::self(); + + if (target) { + waiting_for_status = true; + + xcb_client_message_event_t move; + move.response_type = XCB_CLIENT_MESSAGE; + move.window = target; + move.format = 32; + move.type = atom(QXcbAtom::XdndPosition); + move.window = target; + move.data.data32[0] = connection()->clipboard()->owner(); + move.data.data32[1] = 0; // flags + move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); + move.data.data32[3] = connection()->time(); + move.data.data32[4] = toXdndAction(m->defaultAction(m->dragPrivate()->possible_actions, QGuiApplication::keyboardModifiers())); + DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window; + + source_time = connection()->time(); + + if (w) + handle_xdnd_position(w->window(), &move, false); + else + xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); + } else { + if (m->willDrop) { + m->willDrop = false; + m->updateCursor(); + } + } + DEBUG() << "QDragManager::move leave"; +} + +void QXcbDrag::drop(const QMouseEvent *) +{ + endDrag(); + + if (!current_target) + return; + + xcb_client_message_event_t drop; + drop.response_type = XCB_CLIENT_MESSAGE; + drop.window = current_target; + drop.format = 32; + drop.type = atom(QXcbAtom::XdndDrop); + drop.data.data32[0] = connection()->clipboard()->owner(); + drop.data.data32[1] = 0; // flags + drop.data.data32[2] = connection()->time(); + + drop.data.data32[3] = 0; + drop.data.data32[4] = 0; + + QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target); + + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) + w = 0; + + QDragManager *manager = QDragManager::self(); + + Transaction t = { + connection()->time(), + current_target, + current_proxy_target, + (w ? w->window() : 0), +// current_embedding_widget, + manager->object + }; + transactions.append(t); + restartDropExpiryTimer(); + + if (w) + handleDrop(w->window(), &drop, false); + else + xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop); + + current_target = 0; + current_proxy_target = 0; + source_time = 0; +// current_embedding_widget = 0; + manager->object = 0; +} + +Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const +{ + if (a == atom(QXcbAtom::XdndActionCopy) || a == 0) + return Qt::CopyAction; + if (a == atom(QXcbAtom::XdndActionLink)) + return Qt::LinkAction; + if (a == atom(QXcbAtom::XdndActionMove)) + return Qt::MoveAction; + return Qt::CopyAction; +} + +xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const +{ + switch (a) { + case Qt::CopyAction: + return atom(QXcbAtom::XdndActionCopy); + case Qt::LinkAction: + return atom(QXcbAtom::XdndActionLink); + case Qt::MoveAction: + case Qt::TargetMoveAction: + return atom(QXcbAtom::XdndActionMove); + case Qt::IgnoreAction: + return XCB_NONE; + default: + return atom(QXcbAtom::XdndActionCopy); + } +} + +// timer used to discard old XdndDrop transactions +enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds + +void QXcbDrag::restartDropExpiryTimer() +{ + if (transaction_expiry_timer != -1) + killTimer(transaction_expiry_timer); + transaction_expiry_timer = startTimer(XdndDropTransactionTimeout); +} + +int QXcbDrag::findTransactionByWindow(xcb_window_t window) +{ + int at = -1; + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); + if (t.target == window || t.proxy_target == window) { + at = i; + break; + } + } + return at; +} + +int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp) +{ + int at = -1; + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); + if (t.timestamp == timestamp) { + at = i; + break; + } + } + return at; +} + +#if 0 + +// find an ancestor with XdndAware on it +static Window findXdndAwareParent(Window window) +{ + Window target = 0; + forever { + // check if window has XdndAware + Atom type = 0; + int f; + unsigned long n, a; + unsigned char *data = 0; + if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False, + AnyPropertyType, &type, &f,&n,&a,&data) == Success) { + if (data) + XFree(data); + if (type) { + target = window; + break; + } + } + + // try window's parent + Window root; + Window parent; + Window *children; + uint unused; + if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused)) + break; + if (children) + XFree(children); + if (window == root) + break; + window = parent; + } + return target; +} + + +// for embedding only +static QWidget* current_embedding_widget = 0; +static xcb_client_message_event_t last_enter_event; + + +static bool checkEmbedded(QWidget* w, const XEvent* xe) +{ + if (!w) + return false; + + if (current_embedding_widget != 0 && current_embedding_widget != w) { + current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy; + current_proxy_target = current_target; + qt_xdnd_send_leave(); + current_target = 0; + current_proxy_target = 0; + current_embedding_widget = 0; + } + + QWExtra* extra = ((QExtraWidget*)w)->extraData(); + if (extra && extra->xDndProxy != 0) { + + if (current_embedding_widget != w) { + + last_enter_event.xany.window = extra->xDndProxy; + XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event); + current_embedding_widget = w; + } + + ((XEvent*)xe)->xany.window = extra->xDndProxy; + XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe); + if (currentWindow != w) { + currentWindow = w; + } + return true; + } + current_embedding_widget = 0; + return false; +} +#endif + + +void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *event) +{ + Q_UNUSED(window); + DEBUG() << "handleEnter" << window; + + xdnd_types.clear(); +// motifdnd_active = false; +// last_enter_event.xclient = xe->xclient; + + int version = (int)(event->data.data32[1] >> 24); + if (version > xdnd_version) + return; + + xdnd_dragsource = event->data.data32[0]; + + if (event->data.data32[1] & 1) { + // get the types from XdndTypeList + xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, xdnd_dragsource, + atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM, + 0, xdnd_max_type); + xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0); + if (reply && reply->type != XCB_NONE && reply->format == 32) { + int length = xcb_get_property_value_length(reply) / 4; + if (length > xdnd_max_type) + length = xdnd_max_type; + + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + for (int i = 0; i < length; ++i) + xdnd_types.append(atoms[i]); + } + free(reply); + } else { + // get the types from the message + for(int i = 2; i < 5; i++) { + if (event->data.data32[i]) + xdnd_types.append(event->data.data32[i]); + } + } + for(int i = 0; i < xdnd_types.length(); ++i) + DEBUG() << " " << connection()->atomName(xdnd_types.at(i)); +} + +void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e, bool passive) +{ + QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff); + Q_ASSERT(w); + QRect geometry = w->geometry(); + + p -= geometry.topLeft(); + + // #### +// if (!passive && checkEmbedded(w, e)) +// return; + + if (!w || (/*!w->acceptDrops() &&*/ (w->windowType() == Qt::Desktop))) + return; + + if (e->data.data32[0] != xdnd_dragsource) { + DEBUG("xdnd drag position from unexpected source (%x not %x)", e->data.data32[0], xdnd_dragsource); + return; + } + + // timestamp from the source + if (e->data.data32[3] != XCB_NONE) + target_time /*= X11->userTime*/ = e->data.data32[3]; + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->dropData(); + + xcb_client_message_event_t response; + response.response_type = XCB_CLIENT_MESSAGE; + response.window = xdnd_dragsource; + response.format = 32; + response.type = atom(QXcbAtom::XdndStatus); + response.data.data32[0] = xcb_window(w); + response.data.data32[1] = 0; // flags + response.data.data32[2] = 0; // x, y + response.data.data32[3] = 0; // w, h + response.data.data32[4] = 0; // action + + if (!passive) { // otherwise just reject + QRect answerRect(p + geometry.topLeft(), QSize(1,1)); + + if (manager->object) { + manager->possible_actions = manager->dragPrivate()->possible_actions; + } else { + manager->possible_actions = Qt::DropActions(toDropAction(e->data.data32[4])); + } + QDragMoveEvent me(p, manager->possible_actions, dropData, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + + Qt::DropAction accepted_action = Qt::IgnoreAction; + + currentPosition = p; + + if (w != currentWindow.data()) { + if (currentWindow) { + QDragLeaveEvent e; + QGuiApplication::sendEvent(currentWindow.data(), &e); + } + currentWindow = w; + + last_target_accepted_action = Qt::IgnoreAction; + QDragEnterEvent de(p, manager->possible_actions, dropData, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + QGuiApplication::sendEvent(w, &de); + if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction) + last_target_accepted_action = de.dropAction(); + } + + DEBUG() << "qt_handle_xdnd_position action=" << connection()->atomName(e->data.data32[4]); + + if (last_target_accepted_action != Qt::IgnoreAction) { + me.setDropAction(last_target_accepted_action); + me.accept(); + } + QGuiApplication::sendEvent(w, &me); + if (me.isAccepted()) { + response.data.data32[1] = 1; // yes + accepted_action = me.dropAction(); + last_target_accepted_action = accepted_action; + } else { + response.data.data32[0] = 0; + last_target_accepted_action = Qt::IgnoreAction; + } + answerRect = me.answerRect().translated(geometry.topLeft()).intersected(geometry); + + if (answerRect.left() < 0) + answerRect.setLeft(0); + if (answerRect.right() > 4096) + answerRect.setRight(4096); + if (answerRect.top() < 0) + answerRect.setTop(0); + if (answerRect.bottom() > 4096) + answerRect.setBottom(4096); + if (answerRect.width() < 0) + answerRect.setWidth(0); + if (answerRect.height() < 0) + answerRect.setHeight(0); + +// response.data.data32[2] = (answerRect.x() << 16) + answerRect.y(); +// response.data.data32[3] = (answerRect.width() << 16) + answerRect.height(); + response.data.data32[4] = toXdndAction(accepted_action); + } + + // reset + target_time = XCB_CURRENT_TIME; + + DEBUG() << "sending XdndStatus" << (xdnd_dragsource == connection()->clipboard()->owner()) << xdnd_dragsource + << response.data.data32[1] << connection()->atomName(response.data.data32[4]); + if (xdnd_dragsource == connection()->clipboard()->owner()) + handle_xdnd_status(&response, passive); + else + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + XCB_EVENT_MASK_NO_EVENT, (const char *)&response)); +} + +namespace +{ + class ClientMessageScanner { + public: + ClientMessageScanner(xcb_atom_t a) : atom(a) {} + xcb_atom_t atom; + bool check(xcb_generic_event_t *event) const { + if (!event) + return false; + if ((event->response_type & 0x7f) != XCB_CLIENT_MESSAGE) + return false; + return ((xcb_client_message_event_t *)event)->type == atom; + } + }; +} + +void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event, bool passive) +{ + xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event); + xcb_generic_event_t *nextEvent; + ClientMessageScanner scanner(atom(QXcbAtom::XdndPosition)); + while ((nextEvent = connection()->checkEvent(scanner))) { + if (lastEvent != event) + free(lastEvent); + lastEvent = (xcb_client_message_event_t *)nextEvent; + } + + handle_xdnd_position(w, lastEvent, passive); + if (lastEvent != event) + free(lastEvent); +} + +void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event, bool) +{ + DEBUG("xdndHandleStatus"); + // ignore late status messages + if (event->data.data32[0] && event->data.data32[0] != current_proxy_target) + return; + + Qt::DropAction newAction = (event->data.data32[1] & 0x1) ? toDropAction(event->data.data32[4]) : Qt::IgnoreAction; + + 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, s); + } else { + source_sameanswer = QRect(); + } + QDragManager *manager = QDragManager::self(); + manager->willDrop = (event->data.data32[1] & 0x1); + if (manager->global_accepted_action != newAction) { + manager->global_accepted_action = newAction; + manager->emitActionChanged(newAction); + } + DEBUG() << "willDrop=" << manager->willDrop << "action=" << newAction; + manager->updateCursor(); + waiting_for_status = false; +} + +void QXcbDrag::handleStatus(const xcb_client_message_event_t *event, bool passive) +{ + if (event->window != connection()->clipboard()->owner()) + return; + + xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event); + qDebug() << "handleStatus" << lastEvent->window << lastEvent->data.data32[0]; + xcb_generic_event_t *nextEvent; + ClientMessageScanner scanner(atom(QXcbAtom::XdndStatus)); + while ((nextEvent = connection()->checkEvent(scanner))) { + if (lastEvent != event) + free(lastEvent); + lastEvent = (xcb_client_message_event_t *)nextEvent; + } + + handle_xdnd_status(lastEvent, passive); + if (lastEvent != event) + free(lastEvent); + DEBUG("xdndHandleStatus end"); +} + +void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/) +{ + DEBUG("xdnd leave"); + if (!currentWindow || w != currentWindow.data()) + return; // sanity + + // ### +// if (checkEmbedded(current_embedding_widget, event)) { +// current_embedding_widget = 0; +// currentWindow.clear(); +// return; +// } + + if (event->data.data32[0] != xdnd_dragsource) { + // This often happens - leave other-process window quickly + DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource); + } + + QDragLeaveEvent e; + QGuiApplication::sendEvent(currentWindow.data(), &e); + + xdnd_dragsource = 0; + xdnd_types.clear(); + currentWindow.clear(); +} + +void QXcbDrag::send_leave() +{ + if (!current_target) + return; + + QDragManager *manager = QDragManager::self(); + + xcb_client_message_event_t leave; + leave.response_type = XCB_CLIENT_MESSAGE; + leave.window = current_target; + leave.format = 32; + leave.type = atom(QXcbAtom::XdndLeave); + leave.data.data32[0] = connection()->clipboard()->owner(); + leave.data.data32[1] = 0; // flags + leave.data.data32[2] = 0; // x, y + leave.data.data32[3] = 0; // w, h + leave.data.data32[4] = 0; // just null + + QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target); + + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) + w = 0; + + if (w) + handleLeave(w->window(), (const xcb_client_message_event_t *)&leave, false); + else + xcb_send_event(xcb_connection(), false,current_proxy_target, + XCB_EVENT_MASK_NO_EVENT, (const char *)&leave); + + // reset the drag manager state + manager->willDrop = false; + if (manager->global_accepted_action != Qt::IgnoreAction) + manager->emitActionChanged(Qt::IgnoreAction); + manager->global_accepted_action = Qt::IgnoreAction; + manager->updateCursor(); + current_target = 0; + current_proxy_target = 0; + source_time = XCB_CURRENT_TIME; + waiting_for_status = false; +} + +void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive) +{ + DEBUG("xdndHandleDrop"); + if (!currentWindow) { + xdnd_dragsource = 0; + return; // sanity + } + + // ### +// if (!passive && checkEmbedded(currentWindow, xe)){ +// current_embedding_widget = 0; +// xdnd_dragsource = 0; +// currentWindow = 0; +// return; +// } + const uint32_t *l = event->data.data32; + + QDragManager *manager = QDragManager::self(); + DEBUG("xdnd drop"); + + if (l[0] != xdnd_dragsource) { + DEBUG("xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource); + return; + } + + // update the "user time" from the timestamp in the event. + if (l[2] != 0) + target_time = /*X11->userTime =*/ l[2]; + + if (!passive) { + // this could be a same-application drop, just proxied due to + // some XEMBEDding, so try to find the real QMimeData used + // based on the timestamp for this drop. + QMimeData *dropData = 0; + // ### +// int at = findXdndDropTransactionByTime(target_time); +// if (at != -1) +// dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data; + // if we can't find it, then use the data in the drag manager + if (!dropData) + dropData = manager->dropData(); + + // Drop coming from another app? Update keyboard modifiers. +// if (!qt_xdnd_dragging) { +// QApplicationPrivate::modifier_buttons = currentKeyboardModifiers(); +// } + + QDropEvent de(currentPosition, manager->possible_actions, dropData, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + QGuiApplication::sendEvent(currentWindow.data(), &de); + if (!de.isAccepted()) { + // Ignore a failed drag + manager->global_accepted_action = Qt::IgnoreAction; + } else { + manager->global_accepted_action = de.dropAction(); + } + xcb_client_message_event_t finished; + finished.response_type = XCB_CLIENT_MESSAGE; + finished.window = xdnd_dragsource; + finished.format = 32; + finished.type = atom(QXcbAtom::XdndFinished); + DNDDEBUG << "xdndHandleDrop" + << "currentWindow" << currentWindow.data() + << (currentWindow ? xcb_window(currentWindow.data()) : 0); + finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE; + finished.data.data32[1] = de.isAccepted() ? 1 : 0; // flags + finished.data.data32[2] = toXdndAction(manager->global_accepted_action); + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + XCB_EVENT_MASK_NO_EVENT, (char *)&finished)); + } else { + QDragLeaveEvent e; + QGuiApplication::sendEvent(currentWindow.data(), &e); + } + xdnd_dragsource = 0; + currentWindow.clear(); + waiting_for_status = false; + + // reset + target_time = XCB_CURRENT_TIME; +} + + +void QXcbDrag::handleFinished(const xcb_client_message_event_t *event, bool) +{ + DEBUG("xdndHandleFinished"); + if (event->window != connection()->clipboard()->owner()) + return; + + const unsigned long *l = (const unsigned long *)event->data.data32; + + DNDDEBUG << "xdndHandleFinished, l[0]" << l[0] + << "current_target" << current_target + << "qt_xdnd_current_proxy_targe" << current_proxy_target; + + if (l[0]) { + int at = findTransactionByWindow(l[0]); + if (at != -1) { + restartDropExpiryTimer(); + + Transaction t = transactions.takeAt(at); +// QDragManager *manager = QDragManager::self(); + +// Window target = current_target; +// Window proxy_target = current_proxy_target; +// QWidget *embedding_widget = current_embedding_widget; +// QDrag *currentObject = manager->object; + +// current_target = t.target; +// current_proxy_target = t.proxy_target; +// current_embedding_widget = t.embedding_widget; +// manager->object = t.object; + +// if (!passive) +// (void) checkEmbedded(currentWindow, xe); + +// current_embedding_widget = 0; +// current_target = 0; +// current_proxy_target = 0; + + if (t.object) + t.object->deleteLater(); + +// current_target = target; +// current_proxy_target = proxy_target; +// current_embedding_widget = embedding_widget; +// manager->object = currentObject; + } + } + waiting_for_status = false; +} + + +void QXcbDrag::timerEvent(QTimerEvent* e) +{ + if (e->timerId() == heartbeat && source_sameanswer.isNull()) { + QPointF pos = QCursor::pos(); + QMouseEvent me(QEvent::MouseMove, pos, pos, pos, Qt::LeftButton, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + move(&me); + } else if (e->timerId() == transaction_expiry_timer) { + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); + if (t.targetWindow) { + // dnd within the same process, don't delete these + continue; + } + t.object->deleteLater(); + transactions.removeAt(i--); + } + + killTimer(transaction_expiry_timer); + transaction_expiry_timer = -1; + } +} + +void QXcbDrag::cancel() +{ + DEBUG("QXcbDrag::cancel"); + endDrag(); + + if (current_target) + send_leave(); + + current_target = 0; +} + +#if 0 + +static +Window findRealWindow(const QPoint & pos, Window w, int md) +{ + if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId()) + return 0; + + if (md) { + X11->ignoreBadwindow(); + XWindowAttributes attr; + XGetWindowAttributes(X11->display, w, &attr); + if (X11->badwindow()) + return 0; + + if (attr.map_state == IsViewable + && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) { + { + Atom type = XNone; + int f; + unsigned long n, a; + unsigned char *data; + + XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False, + AnyPropertyType, &type, &f,&n,&a,&data); + if (data) XFree(data); + if (type) + return w; + } + + Window r, p; + Window* c; + uint nc; + if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) { + r=0; + for (uint i=nc; !r && i--;) { + r = findRealWindow(pos-QPoint(attr.x,attr.y), + c[i], md-1); + } + XFree(c); + if (r) + return r; + + // We didn't find a client window! Just use the + // innermost window. + } + + // No children! + return w; + } + } + return 0; +} +#endif + +void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event) +{ + xcb_selection_notify_event_t notify; + notify.response_type = XCB_SELECTION_NOTIFY; + notify.requestor = event->requestor; + notify.selection = event->selection; + notify.target = XCB_NONE; + notify.property = XCB_NONE; + notify.time = event->time; + + QDragManager *manager = QDragManager::self(); + QDrag *currentObject = manager->object; + + // which transaction do we use? (note: -2 means use current manager->object) + int at = -1; + + // figure out which data the requestor is really interested in + if (manager->object && event->time == source_time) { + // requestor wants the current drag data + at = -2; + } else { + // if someone has requested data in response to XdndDrop, find the corresponding transaction. the + // spec says to call XConvertSelection() using the timestamp from the XdndDrop + at = findTransactionByTime(event->time); + if (at == -1) { + // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection() + // that we sent the XdndDrop event to. + at = findTransactionByWindow(event->requestor); + } +// if (at == -1 && event->time == XCB_CURRENT_TIME) { +// // previous Qt versions always requested the data on a child of the target window +// // using CurrentTime... but it could be asking for either drop data or the current drag's data +// Window target = findXdndAwareParent(event->requestor); +// if (target) { +// if (current_target && current_target == target) +// at = -2; +// else +// at = findXdndDropTransactionByWindow(target); +// } +// } + } + if (at >= 0) { + restartDropExpiryTimer(); + + // use the drag object from an XdndDrop tansaction + manager->object = transactions.at(at).object; + } else if (at != -2) { + // no transaction found, we'll have to reject the request + manager->object = 0; + } + if (manager->object) { + xcb_atom_t atomFormat = event->target; + int dataFormat = 0; + QByteArray data; + if (QXcbMime::mimeDataForAtom(connection(), event->target, manager->dragPrivate()->data, + &data, &atomFormat, &dataFormat)) { + int dataSize = data.size() / (dataFormat / 8); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, event->requestor, event->property, + atomFormat, dataFormat, dataSize, (const void *)data.constData()); + notify.property = event->property; + notify.target = atomFormat; + } + } + + // reset manager->object in case we modified it above + manager->object = currentObject; + + xcb_send_event(xcb_connection(), false, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); +} + + +bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) +{ + DNDDEBUG << "xdndEnable" << w << on; + if (on) { + QXcbWindow *xdnd_widget = 0; + if ((w->window()->windowType() == Qt::Desktop)) { + if (desktop_proxy) // *WE* already have one. + return false; + + xcb_grab_server(xcb_connection()); + + // As per Xdnd4, use XdndProxy + xcb_window_t proxy_id = xdndProxy(connection(), w->xcb_window()); + + if (!proxy_id) { + desktop_proxy = new QWindow; + xdnd_widget = static_cast<QXcbWindow *>(desktop_proxy->handle()); + proxy_id = xdnd_widget->xcb_window(); + xcb_atom_t xdnd_proxy = atom(QXcbAtom::XdndProxy); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, w->xcb_window(), xdnd_proxy, + XCB_ATOM_WINDOW, 32, 1, &proxy_id); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, proxy_id, xdnd_proxy, + XCB_ATOM_WINDOW, 32, 1, &proxy_id); + } + + xcb_ungrab_server(xcb_connection()); + } else { + xdnd_widget = w; + } + if (xdnd_widget) { + DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->xcb_window(); + xcb_atom_t atm = xdnd_version; + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, xdnd_widget->xcb_window(), + atom(QXcbAtom::XdndAware), XCB_ATOM_ATOM, 32, 1, &atm); + return true; + } else { + return false; + } + } else { + if ((w->window()->windowType() == Qt::Desktop)) { + xcb_delete_property(xcb_connection(), w->xcb_window(), atom(QXcbAtom::XdndProxy)); + delete desktop_proxy; + desktop_proxy = 0; + } else { + DNDDEBUG << "not deleting XDndAware"; + } + return true; + } +} + + + + +QDropData::QDropData(QXcbDrag *d) + : QXcbMime(), + drag(d) +{ +} + +QDropData::~QDropData() +{ +} + +QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const +{ + QByteArray mime = mimetype.toLatin1(); + QVariant data = /*X11->motifdnd_active + ? X11->motifdndObtainData(mime) + :*/ xdndObtainData(mime, requestedType); + return data; +} + +QVariant QDropData::xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const +{ + QByteArray result; + + QDragManager *manager = QDragManager::self(); + QXcbConnection *c = drag->connection(); + QXcbWindow *xcb_window = c->platformWindowFromId(drag->xdnd_dragsource); + if (xcb_window && manager->object && xcb_window->window()->windowType() != Qt::Desktop) { + QDragPrivate *o = manager->dragPrivate(); + if (o->data->hasFormat(QLatin1String(format))) + result = o->data->data(QLatin1String(format)); + return result; + } + + QList<xcb_atom_t> atoms = drag->xdnd_types; + QByteArray encoding; + xcb_atom_t a = mimeAtomForFormat(c, QLatin1String(format), requestedType, atoms, &encoding); + if (a == XCB_NONE) + return result; + + if (c->clipboard()->getSelectionOwner(drag->atom(QXcbAtom::XdndSelection)) == XCB_NONE) + return result; // should never happen? + + xcb_atom_t xdnd_selection = c->atom(QXcbAtom::XdndSelection); + result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection); + + return mimeConvertToFormat(c, a, result, QLatin1String(format), requestedType, encoding); +} + + +bool QDropData::hasFormat_sys(const QString &format) const +{ + return formats().contains(format); +} + +QStringList QDropData::formats_sys() const +{ + QStringList formats; +// if (X11->motifdnd_active) { +// int i = 0; +// QByteArray fmt; +// while (!(fmt = X11->motifdndFormat(i)).isEmpty()) { +// formats.append(QLatin1String(fmt)); +// ++i; +// } +// } else { + for (int i = 0; i < drag->xdnd_types.size(); ++i) { + QString f = mimeAtomToString(drag->connection(), drag->xdnd_types.at(i)); + if (!formats.contains(f)) + formats.append(f); + } +// } + return formats; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h new file mode 100644 index 0000000000..ce0f8faa54 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBDRAG_H +#define QXCBDRAG_H + +#include <qplatformdrag_qpa.h> +#include <qxcbobject.h> +#include <xcb/xcb.h> +#include <qlist.h> +#include <qpoint.h> +#include <qrect.h> +#include <qsharedpointer.h> +#include <qvector.h> + +QT_BEGIN_NAMESPACE + +class QMouseEvent; +class QWindow; +class QXcbConnection; +class QXcbWindow; +class QDropData; +class QXcbScreen; +class QDrag; + +class QXcbDrag : public QObject, public QXcbObject, public QPlatformDrag +{ +public: + QXcbDrag(QXcbConnection *c); + ~QXcbDrag(); + + virtual QMimeData *platformDropData(); + +// virtual Qt::DropAction drag(QDrag *); + + virtual void startDrag(); + virtual void cancel(); + virtual void move(const QMouseEvent *me); + virtual void drop(const QMouseEvent *me); + void endDrag(); + + void handleEnter(QWindow *window, const xcb_client_message_event_t *event); + void handlePosition(QWindow *w, const xcb_client_message_event_t *event, bool passive); + void handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/); + void handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive); + + void handleStatus(const xcb_client_message_event_t *event, bool passive); + void handleSelectionRequest(const xcb_selection_request_event_t *event); + void handleFinished(const xcb_client_message_event_t *event, bool passive); + + bool dndEnable(QXcbWindow *win, bool on); + +protected: + void timerEvent(QTimerEvent* e); + +private: + friend class QDropData; + + void init(); + + void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event, bool passive); + void handle_xdnd_status(const xcb_client_message_event_t *event, bool); + void send_leave(); + + Qt::DropAction toDropAction(xcb_atom_t atom) const; + xcb_atom_t toXdndAction(Qt::DropAction a) const; + + QWeakPointer<QWindow> currentWindow; + QPoint currentPosition; + + QDropData *dropData; + + QWindow *desktop_proxy; + + xcb_atom_t xdnd_dragsource; + + // the types in this drop. 100 is no good, but at least it's big. + enum { xdnd_max_type = 100 }; + QList<xcb_atom_t> xdnd_types; + + xcb_timestamp_t target_time; + xcb_timestamp_t source_time; + Qt::DropAction last_target_accepted_action; + + // rectangle in which the answer will be the same + QRect source_sameanswer; + bool waiting_for_status; + + // top-level window we sent position to last. + xcb_window_t current_target; + // window to send events to (always valid if current_target) + xcb_window_t current_proxy_target; + + QXcbScreen *current_screen; + + int heartbeat; + bool xdnd_dragging; + + QVector<xcb_atom_t> drag_types; + + struct Transaction + { + xcb_timestamp_t timestamp; + xcb_window_t target; + xcb_window_t proxy_target; + QWindow *targetWindow; +// QWidget *embedding_widget; + QDrag *object; + }; + QList<Transaction> transactions; + + int transaction_expiry_timer; + void restartDropExpiryTimer(); + int findTransactionByWindow(xcb_window_t window); + int findTransactionByTime(xcb_timestamp_t timestamp); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/xcb/qxcbeglsurface.h b/src/plugins/platforms/xcb/qxcbeglsurface.h new file mode 100644 index 0000000000..a1e6c148a2 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbeglsurface.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBEGLSURFACE_H +#define QXCBEGLSURFACE_H + +#include <EGL/egl.h> + +class QXcbEGLSurface +{ +public: + QXcbEGLSurface(EGLDisplay display, EGLSurface surface) + : m_display(display) + , m_surface(surface) + { + } + + ~QXcbEGLSurface() + { + eglDestroySurface(m_display, m_surface); + } + + EGLSurface surface() const { return m_surface; } + +private: + EGLDisplay m_display; + EGLSurface m_surface; +}; + +#endif diff --git a/src/plugins/platforms/xcb/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp new file mode 100644 index 0000000000..569e4fc4e4 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbimage.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbimage.h" +#include <QtGui/QColor> +#include <QtGui/private/qimage_p.h> +#include <QtGui/private/qdrawhelper_p.h> +#ifdef XCB_USE_RENDER +#include <xcb/render.h> +// 'template' is used as a function argument name in xcb_renderutil.h +#define template template_param +// extern "C" is missing too +extern "C" { +#include <xcb/xcb_renderutil.h> +} +#undef template +#endif + +QT_BEGIN_NAMESPACE + +QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, + const xcb_visualtype_t *visual) +{ + const xcb_format_t *format = connection->formatForDepth(depth); + + if (!visual || !format) + return QImage::Format_Invalid; + + if (depth == 32 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 + && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) + return QImage::Format_ARGB32_Premultiplied; + + if (depth == 24 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 + && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) + return QImage::Format_RGB32; + + if (depth == 16 && format->bits_per_pixel == 16 && visual->red_mask == 0xf800 + && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) + return QImage::Format_RGB16; + + return QImage::Format_Invalid; +} + +QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap, + int width, int height, int depth, + const xcb_visualtype_t *visual) +{ + xcb_connection_t *conn = connection->xcb_connection(); + xcb_generic_error_t *error = 0; + + xcb_get_image_cookie_t get_image_cookie = + xcb_get_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, + 0, 0, width, height, 0xffffffff); + + xcb_get_image_reply_t *image_reply = + xcb_get_image_reply(conn, get_image_cookie, &error); + + if (!image_reply) { + if (error) { + connection->handleXcbError(error); + free(error); + } + return QPixmap(); + } + + uint8_t *data = xcb_get_image_data(image_reply); + uint32_t length = xcb_get_image_data_length(image_reply); + + QPixmap result; + + QImage::Format format = qt_xcb_imageFormatForVisual(connection, depth, visual); + if (format != QImage::Format_Invalid) { + uint32_t bytes_per_line = length / height; + QImage image(const_cast<uint8_t *>(data), width, height, bytes_per_line, format); + uint8_t image_byte_order = connection->setup()->image_byte_order; + + // we may have to swap the byte order + if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST) + || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) + { + for (int i=0; i < image.height(); i++) { + switch (format) { + case QImage::Format_RGB16: { + ushort *p = (ushort*)image.scanLine(i); + ushort *end = p + image.width(); + while (p < end) { + *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); + p++; + } + break; + } + case QImage::Format_RGB32: // fall-through + case QImage::Format_ARGB32_Premultiplied: { + uint *p = (uint*)image.scanLine(i); + uint *end = p + image.width(); + while (p < end) { + *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); + p++; + } + break; + } + default: + Q_ASSERT(false); + } + } + } + + // fix-up alpha channel + if (format == QImage::Format_RGB32) { + QRgb *p = (QRgb *)image.bits(); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) + p[x] |= 0xff000000; + p += bytes_per_line / 4; + } + } + + result = QPixmap::fromImage(image.copy()); + } + + free(image_reply); + return result; +} + +xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image) +{ + xcb_connection_t *conn = screen->xcb_connection(); + QImage bitmap = image.convertToFormat(QImage::Format_MonoLSB); + const QRgb c0 = QColor(Qt::black).rgb(); + const QRgb c1 = QColor(Qt::white).rgb(); + if (bitmap.color(0) == c0 && bitmap.color(1) == c1) { + bitmap.invertPixels(); + bitmap.setColor(0, c1); + bitmap.setColor(1, c0); + } + const int width = bitmap.width(); + const int height = bitmap.height(); + const int bytesPerLine = bitmap.bytesPerLine(); + int destLineSize = width / 8; + if (width % 8) + ++destLineSize; + const uchar *map = bitmap.bits(); + uint8_t *buf = new uint8_t[height * destLineSize]; + for (int i = 0; i < height; i++) + memcpy(buf + (destLineSize * i), map + (bytesPerLine * i), destLineSize); + xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, screen->root(), buf, + width, height, 1, 0, 0, 0); + delete[] buf; + return pm; +} + +xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image, + const QPoint &spot) +{ +#ifdef XCB_USE_RENDER + xcb_connection_t *conn = screen->xcb_connection(); + const int w = image.width(); + const int h = image.height(); + xcb_generic_error_t *error = 0; + xcb_render_query_pict_formats_cookie_t formatsCookie = xcb_render_query_pict_formats(conn); + xcb_render_query_pict_formats_reply_t *formatsReply = xcb_render_query_pict_formats_reply(conn, + formatsCookie, + &error); + if (!formatsReply || error) { + qWarning("createCursorXRender: query_pict_formats failed"); + free(formatsReply); + free(error); + return XCB_NONE; + } + xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply, + XCB_PICT_STANDARD_ARGB_32); + if (!fmt) { + qWarning("createCursorXRender: Failed to find format PICT_STANDARD_ARGB_32"); + free(formatsReply); + return XCB_NONE; + } + + QImage img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + xcb_image_t *xi = xcb_image_create(w, h, XCB_IMAGE_FORMAT_Z_PIXMAP, + 32, 32, 32, 32, + QSysInfo::ByteOrder == QSysInfo::BigEndian ? XCB_IMAGE_ORDER_MSB_FIRST : XCB_IMAGE_ORDER_LSB_FIRST, + XCB_IMAGE_ORDER_MSB_FIRST, + 0, 0, 0); + if (!xi) { + qWarning("createCursorXRender: xcb_image_create failed"); + free(formatsReply); + return XCB_NONE; + } + xi->data = (uint8_t *) malloc(xi->stride * h); + if (!xi->data) { + qWarning("createCursorXRender: Failed to malloc() image data"); + xcb_image_destroy(xi); + free(formatsReply); + return XCB_NONE; + } + memcpy(xi->data, img.constBits(), img.byteCount()); + + xcb_pixmap_t pix = xcb_generate_id(conn); + xcb_create_pixmap(conn, 32, pix, screen->root(), w, h); + + xcb_render_picture_t pic = xcb_generate_id(conn); + xcb_render_create_picture(conn, pic, pix, fmt->id, 0, 0); + + xcb_gcontext_t gc = xcb_generate_id(conn); + xcb_create_gc(conn, gc, pix, 0, 0); + xcb_image_put(conn, pix, gc, xi, 0, 0, 0); + xcb_free_gc(conn, gc); + + xcb_cursor_t cursor = xcb_generate_id(conn); + xcb_render_create_cursor(conn, cursor, pic, spot.x(), spot.y()); + + free(xi->data); + xcb_image_destroy(xi); + xcb_render_free_picture(conn, pic); + xcb_free_pixmap(conn, pix); + free(formatsReply); + return cursor; + +#else + Q_UNUSED(screen); + Q_UNUSED(image); + Q_UNUSED(spot); + return XCB_NONE; +#endif +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbimage.h b/src/plugins/platforms/xcb/qxcbimage.h new file mode 100644 index 0000000000..1e7f104084 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbimage.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBIMAGE_H +#define QXCBIMAGE_H + +#include "qxcbscreen.h" +#include <QtCore/QPair> +#include <QtGui/QImage> +#include <QtGui/QPixmap> +#include <xcb/xcb_image.h> + +QT_BEGIN_NAMESPACE + +QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection, + uint8_t depth, const xcb_visualtype_t *visual); +QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap, + int width, int height, int depth, + const xcb_visualtype_t *visual); +xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image); +xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image, + const QPoint &spot); + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 12b63f36ea..b0d377bcac 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -43,76 +43,161 @@ #include "qxcbconnection.h" #include "qxcbscreen.h" #include "qxcbwindow.h" -#include "qxcbwindowsurface.h" +#include "qxcbbackingstore.h" #include "qxcbnativeinterface.h" +#include "qxcbclipboard.h" +#include "qxcbdrag.h" #include <xcb/xcb.h> -#include <private/qpixmap_raster_p.h> - -#include "qgenericunixfontdatabase.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> #include <stdio.h> +//this has to be included before egl, since egl pulls in X headers +#include <QtGui/private/qguiapplication_p.h> + #ifdef XCB_USE_EGL #include <EGL/egl.h> #endif -QXcbIntegration::QXcbIntegration() - : m_connection(new QXcbConnection) +#include <private/qplatforminputcontextfactory_qpa_p.h> +#include <qplatforminputcontext_qpa.h> + +#if defined(XCB_USE_GLX) +#include "qglxintegration.h" +#elif defined(XCB_USE_EGL) +#include "qxcbeglsurface.h" +#include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#endif + +#include <QtGui/QOpenGLContext> +#include <QtGui/QScreen> + +QXcbIntegration::QXcbIntegration(const QStringList ¶meters) + : m_eventDispatcher(createUnixEventDispatcher()) { - foreach (QXcbScreen *screen, m_connection->screens()) - m_screens << screen; + QGuiApplicationPrivate::instance()->setEventDispatcher(m_eventDispatcher); + +#ifdef XCB_USE_XLIB + XInitThreads(); +#endif + + m_connections << new QXcbConnection; + + for (int i = 0; i < parameters.size() - 1; i += 2) { + qDebug() << parameters.at(i) << parameters.at(i+1); + QString display = parameters.at(i) + ':' + parameters.at(i+1); + m_connections << new QXcbConnection(display.toAscii().constData()); + } + + foreach (QXcbConnection *connection, m_connections) + foreach (QXcbScreen *screen, connection->screens()) + screenAdded(screen); m_fontDatabase = new QGenericUnixFontDatabase(); m_nativeInterface = new QXcbNativeInterface; + + m_inputContext = QPlatformInputContextFactory::create(); } QXcbIntegration::~QXcbIntegration() { - delete m_connection; + qDeleteAll(m_connections); } -bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const +QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const { - switch (cap) { - case ThreadedPixmaps: return true; - case OpenGL: return hasOpenGL(); - default: return QPlatformIntegration::hasCapability(cap); - } + return new QXcbWindow(window); } -QPixmapData *QXcbIntegration::createPixmapData(QPixmapData::PixelType type) const +#if defined(XCB_USE_EGL) +class QEGLXcbPlatformContext : public QEGLPlatformContext { - return new QRasterPixmapData(type); -} +public: + QEGLXcbPlatformContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share, + EGLDisplay display, QXcbConnection *c) + : QEGLPlatformContext(glFormat, share, display) + , m_connection(c) + { + Q_XCB_NOOP(m_connection); + } + + void swapBuffers(QPlatformSurface *surface) + { + Q_XCB_NOOP(m_connection); + QEGLPlatformContext::swapBuffers(surface); + Q_XCB_NOOP(m_connection); + } + + bool makeCurrent(QPlatformSurface *surface) + { + Q_XCB_NOOP(m_connection); + bool ret = QEGLPlatformContext::makeCurrent(surface); + Q_XCB_NOOP(m_connection); + return ret; + } + + void doneCurrent() + { + Q_XCB_NOOP(m_connection); + QEGLPlatformContext::doneCurrent(); + Q_XCB_NOOP(m_connection); + } + + EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) + { + return static_cast<QXcbWindow *>(surface)->eglSurface()->surface(); + } -QPlatformWindow *QXcbIntegration::createPlatformWindow(QWidget *widget, WId winId) const +private: + QXcbConnection *m_connection; +}; +#endif + +QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - Q_UNUSED(winId); - return new QXcbWindow(widget); + QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle()); +#if defined(XCB_USE_GLX) + return new QGLXContext(screen, context->format(), context->shareHandle()); +#elif defined(XCB_USE_EGL) + return new QEGLXcbPlatformContext(context->format(), context->shareHandle(), + screen->connection()->egl_display(), screen->connection()); +#elif defined(XCB_USE_DRI2) + return new QDri2Context(context->format(), context->shareHandle()); +#endif } -QWindowSurface *QXcbIntegration::createWindowSurface(QWidget *widget, WId winId) const +QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const { - Q_UNUSED(winId); - return new QXcbWindowSurface(widget); + return new QXcbBackingStore(window); } -QList<QPlatformScreen *> QXcbIntegration::screens() const +bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const { - return m_screens; + switch (cap) { + case ThreadedPixmaps: return true; + case OpenGL: return true; + case ThreadedOpenGL: +#ifdef XCB_POLL_FOR_QUEUED_EVENT + return true; +#else + return false; +#endif + default: return QPlatformIntegration::hasCapability(cap); + } } -void QXcbIntegration::moveToScreen(QWidget *window, int screen) +QAbstractEventDispatcher *QXcbIntegration::guiThreadEventDispatcher() const { - Q_UNUSED(window); - Q_UNUSED(screen); + return m_eventDispatcher; } -bool QXcbIntegration::isVirtualDesktop() +void QXcbIntegration::moveToScreen(QWindow *window, int screen) { - return false; + Q_UNUSED(window); + Q_UNUSED(screen); } QPlatformFontDatabase *QXcbIntegration::fontDatabase() const @@ -120,32 +205,22 @@ QPlatformFontDatabase *QXcbIntegration::fontDatabase() const return m_fontDatabase; } -QPixmap QXcbIntegration::grabWindow(WId window, int x, int y, int width, int height) const +QPlatformNativeInterface * QXcbIntegration::nativeInterface() const { - Q_UNUSED(window); - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(width); - Q_UNUSED(height); - return QPixmap(); + return m_nativeInterface; } +QPlatformClipboard *QXcbIntegration::clipboard() const +{ + return m_connections.at(0)->clipboard(); +} -bool QXcbIntegration::hasOpenGL() const +QPlatformDrag *QXcbIntegration::drag() const { -#if defined(XCB_USE_GLX) - return true; -#elif defined(XCB_USE_EGL) - return m_connection->hasEgl(); -#elif defined(XCB_USE_DRI2) - if (m_connection->hasSupportForDri2()) { - return true; - } -#endif - return false; + return m_connections.at(0)->drag(); } -QPlatformNativeInterface * QXcbIntegration::nativeInterface() const +QPlatformInputContext *QXcbIntegration::inputContext() const { - return m_nativeInterface; + return m_inputContext; } diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index cd68919b54..cad127c28e 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -48,34 +48,40 @@ QT_BEGIN_NAMESPACE class QXcbConnection; +class QAbstractEventDispatcher; class QXcbIntegration : public QPlatformIntegration { public: - QXcbIntegration(); + QXcbIntegration(const QStringList ¶meters); ~QXcbIntegration(); + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + bool hasCapability(Capability cap) const; - QPixmapData *createPixmapData(QPixmapData::PixelType type) const; - QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; - QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + QAbstractEventDispatcher *guiThreadEventDispatcher() const; - QList<QPlatformScreen *> screens() const; - void moveToScreen(QWidget *window, int screen); - bool isVirtualDesktop(); - QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + void moveToScreen(QWindow *window, int screen); QPlatformFontDatabase *fontDatabase() const; QPlatformNativeInterface *nativeInterface()const; + QPlatformClipboard *clipboard() const; + QPlatformDrag *drag() const; + + QPlatformInputContext *inputContext() const; + private: - bool hasOpenGL() const; - QList<QPlatformScreen *> m_screens; - QXcbConnection *m_connection; + QList<QXcbConnection *> m_connections; QPlatformFontDatabase *m_fontDatabase; QPlatformNativeInterface *m_nativeInterface; + + QPlatformInputContext *m_inputContext; + QAbstractEventDispatcher *m_eventDispatcher; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 6bbc3c18ca..907dd0f1b6 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -40,16 +40,17 @@ ****************************************************************************/ #include "qxcbkeyboard.h" - +#include "qxcbwindow.h" +#include "qxcbscreen.h" #include <xcb/xcb_keysyms.h> - #include <X11/keysym.h> - #include <QtGui/QWindowSystemInterface> #include <QtCore/QTextCodec> - +#include <private/qguiapplication_p.h> #include <stdio.h> +#include <qplatforminputcontext_qpa.h> + #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif @@ -898,12 +899,9 @@ QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) : QXcbObject(connection) - , m_alt_mask(0) - , m_super_mask(0) - , m_hyper_mask(0) - , m_meta_mask(0) { m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); + setupModifiers(); } QXcbKeyboard::~QXcbKeyboard() @@ -911,18 +909,113 @@ QXcbKeyboard::~QXcbKeyboard() xcb_key_symbols_free(m_key_symbols); } -// #define XCB_KEYBOARD_DEBUG +void QXcbKeyboard::setupModifiers() +{ + m_alt_mask = 0; + m_super_mask = 0; + m_hyper_mask = 0; + m_meta_mask = 0; + m_mode_switch_mask = 0; + m_num_lock_mask = 0; + m_caps_lock_mask = 0; + + xcb_generic_error_t *error = 0; + xcb_connection_t *conn = xcb_connection(); + xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn); + xcb_get_modifier_mapping_reply_t *modMapReply = + xcb_get_modifier_mapping_reply(conn, modMapCookie, &error); + if (error) { + qWarning("xcb keyboard: failed to get modifier mapping"); + free(error); + return; + } + + // Figure out the modifier mapping, ICCCM 6.6 + typedef QPair<uint, xcb_keycode_t *> SymCodes; + QList<SymCodes> modKeyCodes; + + // for Alt and Meta L and R are the same + modKeyCodes << SymCodes(XK_Alt_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Alt_L)); + modKeyCodes << SymCodes(XK_Meta_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Meta_L)); + modKeyCodes << SymCodes(XK_Super_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Super_L)); + modKeyCodes << SymCodes(XK_Super_R, xcb_key_symbols_get_keycode(m_key_symbols, XK_Super_R)); + modKeyCodes << SymCodes(XK_Hyper_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Hyper_L)); + modKeyCodes << SymCodes(XK_Hyper_R, xcb_key_symbols_get_keycode(m_key_symbols, XK_Hyper_R)); + modKeyCodes << SymCodes(XK_Num_Lock, xcb_key_symbols_get_keycode(m_key_symbols, XK_Num_Lock)); + modKeyCodes << SymCodes(XK_Mode_switch, xcb_key_symbols_get_keycode(m_key_symbols, XK_Mode_switch)); + modKeyCodes << SymCodes(XK_Caps_Lock, xcb_key_symbols_get_keycode(m_key_symbols, XK_Caps_Lock)); + + xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply); + const int w = modMapReply->keycodes_per_modifier; + for (int i = 0; i < modKeyCodes.count(); ++i) { + for (int bit = 0; bit < 8; ++bit) { + uint mask = 1 << bit; + for (int x = 0; x < w; ++x) { + xcb_keycode_t keyCode = modMap[x + bit * w]; + xcb_keycode_t *itk = modKeyCodes.at(i).second; + while (itk && *itk != XCB_NO_SYMBOL) + if (*itk++ == keyCode) + setMask(modKeyCodes.at(i).first, mask); + } + } + } -void QXcbKeyboard::handleKeyEvent(QWidget *widget, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time) + for (int i = 0; i < modKeyCodes.count(); ++i) + free(modKeyCodes.at(i).second); + free(modMapReply); +} + +void QXcbKeyboard::setMask(uint sym, uint mask) { - int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0; + if (m_alt_mask == 0 + && m_meta_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && (sym == XK_Alt_L || sym == XK_Alt_R)) + m_alt_mask = mask; + + if (m_meta_mask == 0 + && m_alt_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && (sym == XK_Meta_L || sym == XK_Meta_R)) + m_meta_mask = mask; + + if (m_super_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_hyper_mask != mask + && (sym == XK_Super_L || sym == XK_Super_R)) + m_super_mask = mask; + + if (m_hyper_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_super_mask != mask + && (sym == XK_Hyper_L || sym == XK_Hyper_R)) + m_hyper_mask = mask; + + if (m_mode_switch_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && sym == XK_Mode_switch) + m_mode_switch_mask = mask; + + if (m_num_lock_mask == 0 && sym == XK_Num_Lock) + m_num_lock_mask = mask; + + if (m_caps_lock_mask == 0 && sym == XK_Caps_Lock) + m_caps_lock_mask = mask; +} - const int altGrOffset = 4; - if (state & 128) - col += altGrOffset; +// #define XCB_KEYBOARD_DEBUG +void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, + quint16 state, xcb_timestamp_t time) +{ Q_XCB_NOOP(connection()); - #ifdef XCB_KEYBOARD_DEBUG printf("key code: %d, state: %d, syms: ", code, state); for (int i = 0; i <= 5; ++i) { @@ -931,43 +1024,105 @@ void QXcbKeyboard::handleKeyEvent(QWidget *widget, QEvent::Type type, xcb_keycod printf("\n"); #endif - Q_XCB_NOOP(connection()); + QByteArray chars; + xcb_keysym_t sym = lookupString(window, state, code, type, &chars); + + + if (QObject* inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext()) { + bool retval; + QMetaObject::invokeMethod(inputContext, "x11FilterEvent", Qt::DirectConnection, + Q_RETURN_ARG(bool, retval), + Q_ARG(uint, sym), + Q_ARG(uint, code), + Q_ARG(uint, state), + Q_ARG(bool, type == QEvent::KeyPress)); + if (retval) + return; + } + Qt::KeyboardModifiers modifiers; + int qtcode = 0; + int count = chars.count(); + QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, + code, 0, state, string.left(count)); +} + +#ifdef XCB_USE_XLIB +extern "C" { + int XLookupString(void *event, char *buf, int count, void *keysym, void *comp); +} +typedef struct { // must match XKeyEvent in Xlib.h + int type; + unsigned long serial; + int send_event; + void *display; + unsigned long window; + unsigned long root; + unsigned long subwindow; + unsigned long time; + int x, y; + int x_root, y_root; + unsigned int state; + unsigned int keycode; + int same_screen; +} FakeXKeyEvent; +#endif + +xcb_keysym_t QXcbKeyboard::lookupString(QWindow *window, uint state, xcb_keycode_t code, + QEvent::Type type, QByteArray *chars) +{ +#ifdef XCB_USE_XLIB + + xcb_keysym_t sym = XCB_NO_SYMBOL; + chars->resize(512); + FakeXKeyEvent event; + memset(&event, 0, sizeof(event)); + event.type = (type == QEvent::KeyRelease ? 3 : 2); + event.display = connection()->xlib_display(); + event.window = static_cast<QXcbWindow *>(window->handle())->xcb_window(); + event.root = connection()->screens().at(0)->root(); + event.state = state; + event.keycode = code; + int count = XLookupString(&event, chars->data(), chars->size(), &sym, 0); + chars->resize(count); + return sym; + +#else + + // No XLookupString available. The following is really incomplete... + + int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0; + const int altGrOffset = 4; + if (state & 128) + col += altGrOffset; xcb_keysym_t sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col); if (sym == XCB_NO_SYMBOL) sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col ^ 0x1); - if (state & XCB_MOD_MASK_LOCK && sym <= 0x7f && isprint(sym)) { if (isupper(sym)) sym = tolower(sym); else sym = toupper(sym); } + return sym; - Q_XCB_NOOP(connection()); - - QByteArray chars; - - Qt::KeyboardModifiers modifiers; - int qtcode = 0; - int count = 0; - - QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); - - QWindowSystemInterface::handleExtendedKeyEvent(widget, time, type, qtcode, modifiers, code, 0, state, string.left(count)); +#endif } -void QXcbKeyboard::handleKeyPressEvent(QWidget *widget, const xcb_key_press_event_t *event) +void QXcbKeyboard::handleKeyPressEvent(QXcbWindow *window, const xcb_key_press_event_t *event) { - handleKeyEvent(widget, QEvent::KeyPress, event->detail, event->state, event->time); + window->updateNetWmUserTime(event->time); + handleKeyEvent(window->window(), QEvent::KeyPress, event->detail, event->state, event->time); } -void QXcbKeyboard::handleKeyReleaseEvent(QWidget *widget, const xcb_key_release_event_t *event) +void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindow *window, const xcb_key_release_event_t *event) { - handleKeyEvent(widget, QEvent::KeyRelease, event->detail, event->state, event->time); + handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time); } void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event) { xcb_refresh_keyboard_mapping(m_key_symbols, const_cast<xcb_mapping_notify_event_t *>(event)); + setupModifiers(); } diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 095c3e4efb..ea5f84e8c2 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -48,26 +48,32 @@ #include <QEvent> +class QWindow; + class QXcbKeyboard : public QXcbObject { public: QXcbKeyboard(QXcbConnection *connection); ~QXcbKeyboard(); - void handleKeyPressEvent(QWidget *widget, const xcb_key_press_event_t *event); - void handleKeyReleaseEvent(QWidget *widget, const xcb_key_release_event_t *event); + void handleKeyPressEvent(QXcbWindow *window, const xcb_key_press_event_t *event); + void handleKeyReleaseEvent(QXcbWindow *window, const xcb_key_release_event_t *event); void handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event); Qt::KeyboardModifiers translateModifiers(int s); private: - void handleKeyEvent(QWidget *widget, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time); + void handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time); int translateKeySym(uint key) const; QString translateKeySym(xcb_keysym_t keysym, uint xmodifiers, int &code, Qt::KeyboardModifiers &modifiers, QByteArray &chars, int &count); + void setupModifiers(); + void setMask(uint sym, uint mask); + xcb_keysym_t lookupString(QWindow *window, uint state, xcb_keycode_t code, + QEvent::Type type, QByteArray *chars); uint m_alt_mask; uint m_super_mask; @@ -75,6 +81,7 @@ private: uint m_meta_mask; uint m_mode_switch_mask; uint m_num_lock_mask; + uint m_caps_lock_mask; xcb_key_symbols_t *m_key_symbols; }; diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp new file mode 100644 index 0000000000..d4f80ca09d --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbmime.h" + +#include <QtCore/QTextCodec> +#include <QtGui/QImageWriter> +#include <QtCore/QBuffer> +#include <qdebug.h> + +#include <X11/Xutil.h> + +#undef XCB_ATOM_STRING +#undef XCB_ATOM_PIXMAP +#undef XCB_ATOM_BITMAP + +QXcbMime::QXcbMime() + : QInternalMimeData() +{ } + +QXcbMime::~QXcbMime() +{} + + + +QString QXcbMime::mimeAtomToString(QXcbConnection *connection, xcb_atom_t a) +{ + if (a == XCB_NONE) + return QString(); + + // special cases for string type + if (a == XCB_ATOM_STRING + || a == connection->atom(QXcbAtom::UTF8_STRING) + || a == connection->atom(QXcbAtom::TEXT)) + return QLatin1String("text/plain"); + + // special case for images + if (a == XCB_ATOM_PIXMAP) + return QLatin1String("image/ppm"); + + QByteArray atomName = connection->atomName(a); + + // special cases for uris + if (atomName == "text/x-moz-url") + atomName = "text/uri-list"; + + return QString::fromLatin1(atomName.constData()); +} + +bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data, + xcb_atom_t *atomFormat, int *dataFormat) +{ + if (!data) + return false; + + bool ret = false; + *atomFormat = a; + *dataFormat = 8; + + if ((a == connection->atom(QXcbAtom::UTF8_STRING) + || a == XCB_ATOM_STRING + || a == connection->atom(QXcbAtom::TEXT)) + && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) { + if (a == connection->atom(QXcbAtom::UTF8_STRING)) { + *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData); + ret = true; + } else if (a == XCB_ATOM_STRING || + a == connection->atom(QXcbAtom::TEXT)) { + // ICCCM says STRING is latin1 + *data = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLatin1(); + ret = true; + } + return ret; + } + + QString atomName = mimeAtomToString(connection, a); + if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) { + *data = QInternalMimeData::renderDataHelper(atomName, mimeData); + if (atomName == QLatin1String("application/x-color")) + *dataFormat = 16; + ret = true; + } else if (atomName == QLatin1String("text/x-moz-url") && + QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) { + QByteArray uri = QInternalMimeData::renderDataHelper( + QLatin1String("text/uri-list"), mimeData).split('\n').first(); + QString mozUri = QString::fromLatin1(uri, uri.size()); + mozUri += QLatin1Char('\n'); + *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2); + ret = true; + } else if ((a == XCB_ATOM_PIXMAP || a == XCB_ATOM_BITMAP) && mimeData->hasImage()) { + ret = true; + } + return ret; +} + +QList<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, const QString &format) +{ + QList<xcb_atom_t> atoms; + atoms.append(connection->internAtom(format.toLatin1())); + + // special cases for strings + if (format == QLatin1String("text/plain")) { + atoms.append(connection->atom(QXcbAtom::UTF8_STRING)); + atoms.append(XCB_ATOM_STRING); + atoms.append(connection->atom(QXcbAtom::TEXT)); + } + + // special cases for uris + if (format == QLatin1String("text/uri-list")) + atoms.append(connection->internAtom("text/x-moz-url")); + + //special cases for images + if (format == QLatin1String("image/ppm")) + atoms.append(XCB_ATOM_PIXMAP); + if (format == QLatin1String("image/pbm")) + atoms.append(XCB_ATOM_BITMAP); + + return atoms; +} + +QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format, + QVariant::Type requestedType, const QByteArray &encoding) +{ + QString atomName = mimeAtomToString(connection, a); +// qDebug() << "mimeConvertDataToFormat" << format << atomName << data; + + if (!encoding.isEmpty() + && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) { + + if (requestedType == QVariant::String) { + QTextCodec *codec = QTextCodec::codecForName(encoding); + if (codec) + return codec->toUnicode(data); + } + + return data; + } + + // special cases for string types + if (format == QLatin1String("text/plain")) { + if (a == connection->atom(QXcbAtom::UTF8_STRING)) + return QString::fromUtf8(data); + if (a == XCB_ATOM_STRING || + a == connection->atom(QXcbAtom::TEXT)) + return QString::fromLatin1(data); + } + + // special case for uri types + if (format == QLatin1String("text/uri-list")) { + if (atomName == QLatin1String("text/x-moz-url")) { + // we expect this as utf16 <url><space><title> + // the first part is a url that should only contain ascci char + // so it should be safe to check that the second char is 0 + // to verify that it is utf16 + if (data.size() > 1 && data.at(1) == 0) + return QString::fromRawData((const QChar *)data.constData(), + data.size() / 2).split(QLatin1Char('\n')).first().toLatin1(); + } + } + + if (atomName == format) + return data; + +#if 0 // ### + // special case for images + if (format == QLatin1String("image/ppm")) { + if (a == XCB_ATOM_PIXMAP && data.size() == sizeof(Pixmap)) { + Pixmap xpm = *((Pixmap*)data.data()); + if (!xpm) + return QByteArray(); + Window root; + int x; + int y; + uint width; + uint height; + uint border_width; + uint depth; + + XGetGeometry(display, xpm, &root, &x, &y, &width, &height, &border_width, &depth); + XImage *ximg = XGetImage(display,xpm,x,y,width,height,AllPlanes,depth==1 ? XYPixmap : ZPixmap); + QImage qimg = QXlibStatic::qimageFromXImage(ximg); + XDestroyImage(ximg); + + QImageWriter imageWriter; + imageWriter.setFormat("PPMRAW"); + QBuffer buf; + buf.open(QIODevice::WriteOnly); + imageWriter.setDevice(&buf); + imageWriter.write(qimg); + return buf.buffer(); + } + } +#endif + return QVariant(); +} + +xcb_atom_t QXcbMime::mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType, + const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding) +{ + requestedEncoding->clear(); + + // find matches for string types + if (format == QLatin1String("text/plain")) { + if (atoms.contains(connection->atom(QXcbAtom::UTF8_STRING))) + return connection->atom(QXcbAtom::UTF8_STRING); + if (atoms.contains(XCB_ATOM_STRING)) + return XCB_ATOM_STRING; + if (atoms.contains(connection->atom(QXcbAtom::TEXT))) + return connection->atom(QXcbAtom::TEXT); + } + + // find matches for uri types + if (format == QLatin1String("text/uri-list")) { + xcb_atom_t a = connection->internAtom(format.toLatin1()); + if (a && atoms.contains(a)) + return a; + a = connection->internAtom("text/x-moz-url"); + if (a && atoms.contains(a)) + return a; + } + + // find match for image + if (format == QLatin1String("image/ppm")) { + if (atoms.contains(XCB_ATOM_PIXMAP)) + return XCB_ATOM_PIXMAP; + } + + // for string/text requests try to use a format with a well-defined charset + // first to avoid encoding problems + if (requestedType == QVariant::String + && format.startsWith(QLatin1String("text/")) + && !format.contains(QLatin1String("charset="))) { + + QString formatWithCharset = format; + formatWithCharset.append(QLatin1String(";charset=utf-8")); + + xcb_atom_t a = connection->internAtom(formatWithCharset.toLatin1()); + if (a && atoms.contains(a)) { + *requestedEncoding = "utf-8"; + return a; + } + } + + xcb_atom_t a = connection->internAtom(format.toLatin1()); + if (a && atoms.contains(a)) + return a; + + return 0; +} diff --git a/src/plugins/platforms/xcb/qxcbmime.h b/src/plugins/platforms/xcb/qxcbmime.h new file mode 100644 index 0000000000..4ab38ed803 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbmime.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBMIME_H +#define QXCBMIME_H + +#include <private/qdnd_p.h> + +#include <QtGui/QClipboard> + +#include "qxcbintegration.h" +#include "qxcbconnection.h" + +class QXcbMime : public QInternalMimeData { + Q_OBJECT +public: + QXcbMime(); + ~QXcbMime(); + + static QList<xcb_atom_t> mimeAtomsForFormat(QXcbConnection *connection, const QString &format); + static QString mimeAtomToString(QXcbConnection *connection, xcb_atom_t a); + static bool mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data, + xcb_atom_t *atomFormat, int *dataFormat); + static QVariant mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format, + QVariant::Type requestedType, const QByteArray &encoding); + static xcb_atom_t mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType, + const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding); +}; + +#endif // QXCBMIME_H diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 6423f1b798..3c9cdfd257 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -43,13 +43,16 @@ #include "qxcbscreen.h" -#include <QtGui/private/qapplication_p.h> +#include <private/qguiapplication_p.h> #include <QtCore/QMap> #include <QtCore/QDebug> +#include <QtGui/qopenglcontext.h> +#include <QtGui/qscreen.h> + #if defined(XCB_USE_EGL) -#include "../eglconvenience/qeglplatformcontext.h" +#include "QtPlatformSupport/private/qeglplatformcontext_p.h" #elif defined (XCB_USE_DRI2) #include "qdri2context.h" #endif @@ -71,29 +74,41 @@ public: Q_GLOBAL_STATIC(QXcbResourceMap, qXcbResourceMap) -void *QXcbNativeInterface::nativeResourceForWidget(const QByteArray &resourceString, QWidget *widget) +void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context) +{ + QByteArray lowerCaseResource = resourceString.toLower(); + ResourceType resource = qXcbResourceMap()->value(lowerCaseResource); + void *result = 0; + switch(resource) { + case EglContext: + result = eglContextForContext(context); + break; + default: + result = 0; + } + return result; +} + +void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) { QByteArray lowerCaseResource = resourceString.toLower(); ResourceType resource = qXcbResourceMap()->value(lowerCaseResource); void *result = 0; switch(resource) { case Display: - result = displayForWidget(widget); + result = displayForWindow(window); break; case EglDisplay: - result = eglDisplayForWidget(widget); + result = eglDisplayForWindow(window); break; case Connection: - result = connectionForWidget(widget); + result = connectionForWindow(window); break; case Screen: - result = qPlatformScreenForWidget(widget); + result = qPlatformScreenForWindow(window); break; case GraphicsDevice: - result = graphicsDeviceForWidget(widget); - break; - case EglContext: - result = eglContextForWidget(widget); + result = graphicsDeviceForWindow(window); break; default: result = 0; @@ -101,75 +116,76 @@ void *QXcbNativeInterface::nativeResourceForWidget(const QByteArray &resourceStr return result; } -QXcbScreen *QXcbNativeInterface::qPlatformScreenForWidget(QWidget *widget) +QXcbScreen *QXcbNativeInterface::qPlatformScreenForWindow(QWindow *window) { QXcbScreen *screen; - if (widget) { - screen = static_cast<QXcbScreen *>(QPlatformScreen::platformScreenForWidget(widget)); - }else { - screen = static_cast<QXcbScreen *>(QApplicationPrivate::platformIntegration()->screens()[0]); + if (window) { + screen = static_cast<QXcbScreen *>(window->screen()->handle()); + } else { + screen = static_cast<QXcbScreen *>(QGuiApplication::primaryScreen()->handle()); } return screen; } -void *QXcbNativeInterface::displayForWidget(QWidget *widget) +void *QXcbNativeInterface::displayForWindow(QWindow *window) { #if defined(XCB_USE_XLIB) - QXcbScreen *screen = qPlatformScreenForWidget(widget); + QXcbScreen *screen = qPlatformScreenForWindow(window); return screen->connection()->xlib_display(); #else - Q_UNUSED(widget); + Q_UNUSED(window); return 0; #endif } -void *QXcbNativeInterface::eglDisplayForWidget(QWidget *widget) +void *QXcbNativeInterface::eglDisplayForWindow(QWindow *window) { #if defined(XCB_USE_DRI2) || defined(XCB_USE_EGL) - QXcbScreen *screen = qPlatformScreenForWidget(widget); + QXcbScreen *screen = qPlatformScreenForWindow(window); return screen->connection()->egl_display(); #else - Q_UNUSED(widget) + Q_UNUSED(window) return 0; #endif } -void *QXcbNativeInterface::connectionForWidget(QWidget *widget) +void *QXcbNativeInterface::connectionForWindow(QWindow *window) { - QXcbScreen *screen = qPlatformScreenForWidget(widget); + QXcbScreen *screen = qPlatformScreenForWindow(window); return screen->xcb_connection(); } -void *QXcbNativeInterface::screenForWidget(QWidget *widget) +void *QXcbNativeInterface::screenForWindow(QWindow *window) { - QXcbScreen *screen = qPlatformScreenForWidget(widget); + QXcbScreen *screen = qPlatformScreenForWindow(window); return screen->screen(); } -void *QXcbNativeInterface::graphicsDeviceForWidget(QWidget *widget) +void *QXcbNativeInterface::graphicsDeviceForWindow(QWindow *window) { #if defined(XCB_USE_DRI2) - QXcbScreen *screen = qPlatformScreenForWidget(widget); + QXcbScreen *screen = qPlatformScreenForWindow(window); QByteArray deviceName = screen->connection()->dri2DeviceName(); return deviceName.data(); #else - Q_UNUSED(widget); + Q_UNUSED(window); return 0; #endif } -void * QXcbNativeInterface::eglContextForWidget(QWidget *widget) +void * QXcbNativeInterface::eglContextForContext(QOpenGLContext *context) { - Q_ASSERT(widget); - if (!widget->platformWindow()) { - qDebug() << "QPlatformWindow does not exist for widget" << widget - << "cannot return EGLContext"; - return 0; - } - QPlatformGLContext *platformContext = widget->platformWindow()->glContext(); + Q_ASSERT(context); +#if defined(XCB_USE_EGL) + QEGLPlatformContext *eglPlatformContext = static_cast<QEGLPlatformContext *>(context->handle()); + return eglPlatformContext->eglContext(); +#endif +#if 0 + Q_ASSERT(window); + QPlatformOpenGLContext *platformContext = window->glContext()->handle(); if (!platformContext) { - qDebug() << "QPlatformWindow" << widget->platformWindow() << "does not have a glContext" + qDebug() << "QWindow" << window << "does not have a glContext" << "cannot return EGLContext"; return 0; } @@ -182,4 +198,7 @@ void * QXcbNativeInterface::eglContextForWidget(QWidget *widget) #else return 0; #endif +#else + return 0; +#endif } diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index 3fb0fa5e2d..8dec83267a 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -59,17 +59,19 @@ public: EglContext }; - void *nativeResourceForWidget(const QByteArray &resourceString, QWidget *widget); + void *nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context); + void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window); - void *displayForWidget(QWidget *widget); - void *eglDisplayForWidget(QWidget *widget); - void *connectionForWidget(QWidget *widget); - void *screenForWidget(QWidget *widget); - void *graphicsDeviceForWidget(QWidget *widget); - void *eglContextForWidget(QWidget *widget); + void *displayForWindow(QWindow *window); + void *eglDisplayForWindow(QWindow *window); + void *connectionForWindow(QWindow *window); + void *screenForWindow(QWindow *window); + void *graphicsDeviceForWindow(QWindow *window); + + void *eglContextForContext(QOpenGLContext *context); private: - static QXcbScreen *qPlatformScreenForWidget(QWidget *widget); + static QXcbScreen *qPlatformScreenForWindow(QWindow *window); }; #endif // QXCBNATIVEINTERFACE_H diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 9a4858123b..42259feda8 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -40,6 +40,9 @@ ****************************************************************************/ #include "qxcbscreen.h" +#include "qxcbwindow.h" +#include "qxcbcursor.h" +#include "qxcbimage.h" #include <stdio.h> @@ -102,10 +105,104 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num free(reply); m_syncRequestSupported = m_windowManagerName != QLatin1String("KWin"); + + m_clientLeader = xcb_generate_id(xcb_connection()); + Q_XCB_CALL2(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, + m_clientLeader, + m_screen->root, + 0, 0, 1, 1, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + m_screen->root_visual, + 0, 0), connection); + + Q_XCB_CALL2(xcb_change_property(xcb_connection(), + XCB_PROP_MODE_REPLACE, + m_clientLeader, + atom(QXcbAtom::WM_CLIENT_LEADER), + XCB_ATOM_WINDOW, + 32, + 1, + &m_clientLeader), connection); + + xcb_depth_iterator_t depth_iterator = + xcb_screen_allowed_depths_iterator(screen); + + while (depth_iterator.rem) { + xcb_depth_t *depth = depth_iterator.data; + xcb_visualtype_iterator_t visualtype_iterator = + xcb_depth_visuals_iterator(depth); + + while (visualtype_iterator.rem) { + xcb_visualtype_t *visualtype = visualtype_iterator.data; + m_visuals.insert(visualtype->visual_id, *visualtype); + xcb_visualtype_next(&visualtype_iterator); + } + + xcb_depth_next(&depth_iterator); + } + + m_cursor = new QXcbCursor(connection, this); } QXcbScreen::~QXcbScreen() { + delete m_cursor; +} + + +QWindow *QXcbScreen::topLevelAt(const QPoint &p) const +{ + xcb_window_t root = m_screen->root; + + int x = p.x(); + int y = p.y(); + + xcb_generic_error_t *error; + + xcb_window_t parent = root; + xcb_window_t child = root; + + do { + xcb_translate_coordinates_cookie_t translate_cookie = + xcb_translate_coordinates(xcb_connection(), parent, child, x, y); + + xcb_translate_coordinates_reply_t *translate_reply = + xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, &error); + + if (!translate_reply) { + if (error) { + connection()->handleXcbError(error); + free(error); + } + return 0; + } + + parent = child; + child = translate_reply->child; + x = translate_reply->dst_x; + y = translate_reply->dst_y; + + free(translate_reply); + + if (!child || child == root) + return 0; + + QPlatformWindow *platformWindow = connection()->platformWindowFromId(child); + if (platformWindow) + return platformWindow->window(); + } while (parent != child); + + return 0; +} + +const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const +{ + QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid); + if (it == m_visuals.constEnd()) + return 0; + return &*it; } QRect QXcbScreen::geometry() const @@ -132,3 +229,126 @@ int QXcbScreen::screenNumber() const { return m_number; } + +QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const +{ + if (width == 0 || height == 0) + return QPixmap(); + + xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(xcb_connection(), window); + + xcb_generic_error_t *error; + xcb_get_geometry_reply_t *reply = + xcb_get_geometry_reply(xcb_connection(), geometry_cookie, &error); + + if (!reply) { + if (error) { + connection()->handleXcbError(error); + free(error); + } + return QPixmap(); + } + + if (width < 0) + width = reply->width - x; + if (height < 0) + height = reply->height - y; + + // TODO: handle multiple screens + QXcbScreen *screen = const_cast<QXcbScreen *>(this); + xcb_window_t root = screen->root(); + geometry_cookie = xcb_get_geometry(xcb_connection(), root); + xcb_get_geometry_reply_t *root_reply = + xcb_get_geometry_reply(xcb_connection(), geometry_cookie, &error); + + if (!root_reply) { + if (error) { + connection()->handleXcbError(error); + free(error); + } + free(reply); + return QPixmap(); + } + + if (reply->depth == root_reply->depth) { + // if the depth of the specified window and the root window are the + // same, grab pixels from the root window (so that we get the any + // overlapping windows and window manager frames) + + // map x and y to the root window + xcb_translate_coordinates_cookie_t translate_cookie = + xcb_translate_coordinates(xcb_connection(), window, root, x, y); + + xcb_translate_coordinates_reply_t *translate_reply = + xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, &error); + + if (!translate_reply) { + if (error) { + connection()->handleXcbError(error); + free(error); + } + free(reply); + free(root_reply); + return QPixmap(); + } + + x = translate_reply->dst_x; + y = translate_reply->dst_y; + + window = root; + + free(translate_reply); + free(reply); + reply = root_reply; + } else { + free(root_reply); + root_reply = 0; + } + + xcb_get_window_attributes_reply_t *attributes_reply = + xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes(xcb_connection(), window), &error); + + if (!attributes_reply) { + if (error) { + connection()->handleXcbError(error); + free(error); + } + free(reply); + return QPixmap(); + } + + const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual); + free(attributes_reply); + + xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection()); + error = xcb_request_check(xcb_connection(), xcb_create_pixmap_checked(xcb_connection(), reply->depth, pixmap, window, width, height)); + if (error) { + connection()->handleXcbError(error); + free(error); + } + + uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE; + uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS }; + + xcb_gcontext_t gc = xcb_generate_id(xcb_connection()); + xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list); + + error = xcb_request_check(xcb_connection(), xcb_copy_area_checked(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height)); + if (error) { + connection()->handleXcbError(error); + free(error); + } + + QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual); + + free(reply); + xcb_free_gc(xcb_connection(), gc); + xcb_free_pixmap(xcb_connection(), pixmap); + + return result; +} + +QString QXcbScreen::name() const +{ + return connection()->displayName() + QLatin1String(".") + QString::number(screenNumber()); +} diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index a78ee822f4..07d855b398 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -50,6 +50,7 @@ #include "qxcbobject.h" class QXcbConnection; +class QXcbCursor; class QXcbScreen : public QXcbObject, public QPlatformScreen { @@ -57,6 +58,10 @@ public: QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int number); ~QXcbScreen(); + QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + + QWindow *topLevelAt(const QPoint &point) const; + QRect geometry() const; int depth() const; QImage::Format format() const; @@ -67,14 +72,23 @@ public: xcb_screen_t *screen() const { return m_screen; } xcb_window_t root() const { return m_screen->root; } + xcb_window_t clientLeader() const { return m_clientLeader; } + QString windowManagerName() const { return m_windowManagerName; } bool syncRequestSupported() const { return m_syncRequestSupported; } + const xcb_visualtype_t *visualForId(xcb_visualid_t) const; + + QString name() const; + private: xcb_screen_t *m_screen; int m_number; QString m_windowManagerName; bool m_syncRequestSupported; + xcb_window_t m_clientLeader; + QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals; + QXcbCursor *m_cursor; }; #endif diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 20e4187541..db8d37e817 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -42,9 +42,13 @@ #include "qxcbwindow.h" #include <QtDebug> +#include <QScreen> #include "qxcbconnection.h" #include "qxcbscreen.h" +#include "qxcbdrag.h" +#include "qxcbwmsupport.h" + #ifdef XCB_USE_DRI2 #include "qdri2context.h" #endif @@ -61,9 +65,10 @@ #define xcb_set_wm_hints xcb_icccm_set_wm_hints #endif -#include <private/qapplication_p.h> -#include <private/qwindowsurface_p.h> +#include <private/qguiapplication_p.h> +#include <private/qwindow_p.h> +#include <QtGui/QPlatformBackingStore> #include <QtGui/QWindowSystemInterface> #include <stdio.h> @@ -75,38 +80,71 @@ #if defined(XCB_USE_GLX) #include "qglxintegration.h" -#include "qglxconvenience.h" +#include <QtPlatformSupport/private/qglxconvenience_p.h> #elif defined(XCB_USE_EGL) -#include "../eglconvenience/qeglplatformcontext.h" -#include "../eglconvenience/qeglconvenience.h" -#include "../eglconvenience/qxlibeglintegration.h" +#include "qxcbeglsurface.h" +#include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtPlatformSupport/private/qxlibeglintegration_p.h> #endif +#define XCOORD_MAX 16383 + +//#ifdef NET_WM_STATE_DEBUG + // Returns true if we should set WM_TRANSIENT_FOR on \a w -static inline bool isTransient(const QWidget *w) +static inline bool isTransient(const QWindow *w) { - return ((w->windowType() == Qt::Dialog - || w->windowType() == Qt::Sheet - || w->windowType() == Qt::Tool - || w->windowType() == Qt::SplashScreen - || w->windowType() == Qt::ToolTip - || w->windowType() == Qt::Drawer - || w->windowType() == Qt::Popup) - && !w->testAttribute(Qt::WA_X11BypassTransientForHint)); + return w->windowType() == Qt::Dialog + || w->windowType() == Qt::Sheet + || w->windowType() == Qt::Tool + || w->windowType() == Qt::SplashScreen + || w->windowType() == Qt::ToolTip + || w->windowType() == Qt::Drawer + || w->windowType() == Qt::Popup; } -QXcbWindow::QXcbWindow(QWidget *tlw) - : QPlatformWindow(tlw) - , m_context(0) +QXcbWindow::QXcbWindow(QWindow *window) + : QPlatformWindow(window) + , m_window(0) + , m_syncCounter(0) + , m_mapped(false) + , m_netWmUserTimeWindow(XCB_NONE) +#if defined(XCB_USE_EGL) + , m_eglSurface(0) +#endif { - m_screen = static_cast<QXcbScreen *>(QPlatformScreen::platformScreenForWidget(tlw)); + m_screen = static_cast<QXcbScreen *>(window->screen()->handle()); setConnection(m_screen->connection()); - const quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK; + create(); +} + +void QXcbWindow::create() +{ + destroy(); + + m_windowState = Qt::WindowNoState; + m_dirtyFrameMargins = true; + + Qt::WindowType type = window()->windowType(); + + if (type == Qt::Desktop) { + m_window = m_screen->root(); + m_depth = m_screen->screen()->root_depth; + m_imageFormat = (m_depth == 32) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + connection()->addWindow(m_window, this); + return; + } + + const quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK; const quint32 values[] = { // XCB_CW_BACK_PIXMAP XCB_NONE, + // XCB_CW_OVERRIDE_REDIRECT + type == Qt::Popup || type == Qt::ToolTip, + // XCB_CW_SAVE_UNDER + type == Qt::Popup || type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer, // XCB_CW_EVENT_MASK XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY @@ -117,20 +155,33 @@ QXcbWindow::QXcbWindow(QWidget *tlw) | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW + | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE }; -#if defined(XCB_USE_GLX) || defined(XCB_USE_EGL) - if (tlw->platformWindowFormat().windowApi() == QPlatformWindowFormat::OpenGL - && QApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL) - || tlw->platformWindowFormat().alpha()) + QRect rect = window()->geometry(); + QPlatformWindow::setGeometry(rect); + + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); + rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); + + xcb_window_t xcb_parent_id = m_screen->root(); + if (parent()) + xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window(); + + m_requestedFormat = window()->format(); + +#if (defined(XCB_USE_GLX) || defined(XCB_USE_EGL)) && defined(XCB_USE_XLIB) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL) + || window()->format().hasAlpha()) { #if defined(XCB_USE_GLX) - XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen),m_screen->screenNumber(), tlw->platformWindowFormat()); + XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen),m_screen->screenNumber(), window()->format()); + #elif defined(XCB_USE_EGL) EGLDisplay eglDisplay = connection()->egl_display(); - EGLConfig eglConfig = q_configFromQPlatformWindowFormat(eglDisplay,tlw->platformWindowFormat(),true); + EGLConfig eglConfig = q_configFromGLFormat(eglDisplay, window()->format(), true); VisualID id = QXlibEglIntegration::getCompatibleVisualId(DISPLAY_FROM_XCB(this), eglDisplay, eglConfig); XVisualInfo visualInfoTemplate; @@ -143,18 +194,16 @@ QXcbWindow::QXcbWindow(QWidget *tlw) #endif //XCB_USE_GLX if (visualInfo) { m_depth = visualInfo->depth; - m_format = (m_depth == 32) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), m_screen->root(), visualInfo->visual, AllocNone); + m_imageFormat = (m_depth == 32) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), xcb_parent_id, visualInfo->visual, AllocNone); XSetWindowAttributes a; a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber()); a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber()); a.colormap = cmap; - m_window = XCreateWindow(DISPLAY_FROM_XCB(this), m_screen->root(), tlw->x(), tlw->y(), tlw->width(), tlw->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); - - printf("created GL window: %d\n", m_window); } else { qFatal("no window!"); } @@ -163,25 +212,25 @@ QXcbWindow::QXcbWindow(QWidget *tlw) { m_window = xcb_generate_id(xcb_connection()); m_depth = m_screen->screen()->root_depth; - m_format = (m_depth == 32) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + m_imageFormat = (m_depth == 32) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; Q_XCB_CALL(xcb_create_window(xcb_connection(), XCB_COPY_FROM_PARENT, // depth -- same as root m_window, // window id - m_screen->root(), // parent window id - tlw->x(), - tlw->y(), - tlw->width(), - tlw->height(), + xcb_parent_id, // parent window id + rect.x(), + rect.y(), + rect.width(), + rect.height(), 0, // border width XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class m_screen->screen()->root_visual, // visual 0, // value mask 0)); // value list - - printf("created regular window: %d\n", m_window); } + connection()->addWindow(m_window, this); + Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values)); xcb_atom_t properties[4]; @@ -193,7 +242,7 @@ QXcbWindow::QXcbWindow(QWidget *tlw) if (m_screen->syncRequestSupported()) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); - if (tlw->windowFlags() & Qt::WindowContextHelpButtonHint) + if (window()->windowFlags() & Qt::WindowContextHelpButtonHint) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP); Q_XCB_CALL(xcb_change_property(xcb_connection(), @@ -221,29 +270,49 @@ QXcbWindow::QXcbWindow(QWidget *tlw) &m_syncCounter)); } - if (isTransient(tlw) && tlw->parentWidget()) { - // ICCCM 4.1.2.6 - QWidget *p = tlw->parentWidget()->window(); - xcb_window_t parentWindow = p->winId(); - Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, - 1, &parentWindow)); - - } - // set the PID to let the WM kill the application if unresponsive long pid = getpid(); Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, &pid)); + + xcb_wm_hints_t hints; + memset(&hints, 0, sizeof(hints)); + xcb_wm_hints_set_normal(&hints); + + xcb_set_wm_hints(xcb_connection(), m_window, &hints); + + xcb_window_t leader = m_screen->clientLeader(); + Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, + 1, &leader)); + + setWindowFlags(window()->windowFlags()); + setWindowTitle(window()->windowTitle()); + setWindowState(window()->windowState()); + + connection()->drag()->dndEnable(this, true); } QXcbWindow::~QXcbWindow() { - delete m_context; - if (m_screen->syncRequestSupported()) + destroy(); +} + +void QXcbWindow::destroy() +{ + if (m_syncCounter && m_screen->syncRequestSupported()) Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter)); - Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window)); + if (m_window) { + connection()->removeWindow(m_window); + Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window)); + } + m_mapped = false; + +#if defined(XCB_USE_EGL) + delete m_eglSurface; + m_eglSurface = 0; +#endif } void QXcbWindow::setGeometry(const QRect &rect) @@ -251,40 +320,182 @@ void QXcbWindow::setGeometry(const QRect &rect) QPlatformWindow::setGeometry(rect); const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - const quint32 values[] = { rect.x(), rect.y(), rect.width(), rect.height() }; + const quint32 values[] = { rect.x(), + rect.y(), + qBound(1, rect.width(), XCOORD_MAX), + qBound(1, rect.height(), XCOORD_MAX) }; Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values)); } +QMargins QXcbWindow::frameMargins() const +{ + if (m_dirtyFrameMargins) { + xcb_window_t window = m_window; + xcb_window_t parent = m_window; + + bool foundRoot = false; + + const QVector<xcb_window_t> &virtualRoots = + connection()->wmSupport()->virtualRoots(); + + while (!foundRoot) { + xcb_query_tree_cookie_t cookie = xcb_query_tree(xcb_connection(), parent); + + xcb_generic_error_t *error; + xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, &error); + if (reply) { + if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1) { + foundRoot = true; + } else { + window = parent; + parent = reply->parent; + } + + free(reply); + } else { + if (error) { + connection()->handleXcbError(error); + free(error); + } + + m_dirtyFrameMargins = false; + m_frameMargins = QMargins(); + return m_frameMargins; + } + } + + QPoint offset; + + xcb_generic_error_t *error; + xcb_translate_coordinates_reply_t *reply = + xcb_translate_coordinates_reply( + xcb_connection(), + xcb_translate_coordinates(xcb_connection(), window, parent, 0, 0), + &error); + + if (reply) { + offset = QPoint(reply->dst_x, reply->dst_y); + free(reply); + } else if (error) { + free(error); + } + + xcb_get_geometry_reply_t *geom = + xcb_get_geometry_reply( + xcb_connection(), + xcb_get_geometry(xcb_connection(), parent), + &error); + + if (geom) { + // -- + // add the border_width for the window managers frame... some window managers + // do not use a border_width of zero for their frames, and if we the left and + // top strut, we ensure that pos() is absolutely correct. frameGeometry() + // will still be incorrect though... perhaps i should have foffset as well, to + // indicate the frame offset (equal to the border_width on X). + // - Brad + // -- copied from qwidget_x11.cpp + + int left = offset.x() + geom->border_width; + int top = offset.y() + geom->border_width; + int right = geom->width + geom->border_width - geometry().width() - offset.x(); + int bottom = geom->height + geom->border_width - geometry().height() - offset.y(); + + m_frameMargins = QMargins(left, top, right, bottom); + + free(geom); + } else if (error) { + free(error); + } + + m_dirtyFrameMargins = false; + } + + return m_frameMargins; +} + void QXcbWindow::setVisible(bool visible) { - xcb_wm_hints_t hints; - if (visible) { - if (widget()->isMinimized()) + if (visible) + show(); + else + hide(); +} + +void QXcbWindow::show() +{ + if (window()->isTopLevel()) { + xcb_get_property_cookie_t cookie = xcb_get_wm_hints(xcb_connection(), m_window); + + xcb_generic_error_t *error; + + xcb_wm_hints_t hints; + xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, &error); + + if (error) { + connection()->handleXcbError(error); + free(error); + } + + m_dirtyFrameMargins = true; + + if (window()->windowState() & Qt::WindowMinimized) xcb_wm_hints_set_iconic(&hints); else xcb_wm_hints_set_normal(&hints); + xcb_set_wm_hints(xcb_connection(), m_window, &hints); - Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); - connection()->sync(); - } else { - Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); - - // send synthetic UnmapNotify event according to icccm 4.1.4 - xcb_unmap_notify_event_t event; - event.response_type = XCB_UNMAP_NOTIFY; - event.sequence = 0; // does this matter? - event.event = m_screen->root(); - event.window = m_window; - event.from_configure = false; - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_screen->root(), - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); - xcb_flush(xcb_connection()); + // update WM_NORMAL_HINTS + propagateSizeHints(); + + // update WM_TRANSIENT_FOR + if (window()->transientParent() && isTransient(window())) { + QXcbWindow *transientXcbParent = static_cast<QXcbWindow *>(window()->transientParent()->handle()); + if (transientXcbParent) { + // ICCCM 4.1.2.6 + xcb_window_t parentWindow = transientXcbParent->xcb_window(); + + // todo: set transient for group (wm_client_leader) if no parent, a la qwidget_x11.cpp + Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, + 1, &parentWindow)); + } + } + + // update _MOTIF_WM_HINTS + updateMotifWmHintsBeforeMap(); + + // update _NET_WM_STATE + updateNetWmStateBeforeMap(); } + + Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); + xcb_flush(xcb_connection()); + + connection()->sync(); +} + +void QXcbWindow::hide() +{ + Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); + + // send synthetic UnmapNotify event according to icccm 4.1.4 + xcb_unmap_notify_event_t event; + event.response_type = XCB_UNMAP_NOTIFY; + event.event = m_screen->root(); + event.window = m_window; + event.from_configure = false; + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_screen->root(), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); + + xcb_flush(xcb_connection()); + + m_mapped = false; } -struct QtMWMHints { +struct QtMotifWmHints { quint32 flags, functions, decorations; qint32 input_mode; quint32 status; @@ -317,33 +528,142 @@ enum { MWM_INPUT_FULL_APPLICATION_MODAL = 3L }; +static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window) +{ + QtMotifWmHints hints; + + xcb_get_property_cookie_t get_cookie = + xcb_get_property(c->xcb_connection(), 0, window, c->atom(QXcbAtom::_MOTIF_WM_HINTS), + c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20); + + xcb_generic_error_t *error; + + xcb_get_property_reply_t *reply = + xcb_get_property_reply(c->xcb_connection(), get_cookie, &error); + + if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) { + hints = *((QtMotifWmHints *)xcb_get_property_value(reply)); + } else if (error) { + c->handleXcbError(error); + free(error); + + hints.flags = 0L; + hints.functions = MWM_FUNC_ALL; + hints.decorations = MWM_DECOR_ALL; + hints.input_mode = 0L; + hints.status = 0L; + } + + free(reply); + + return hints; +} + +static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints) +{ + if (hints.flags != 0l) { + Q_XCB_CALL2(xcb_change_property(c->xcb_connection(), + XCB_PROP_MODE_REPLACE, + window, + c->atom(QXcbAtom::_MOTIF_WM_HINTS), + c->atom(QXcbAtom::_MOTIF_WM_HINTS), + 32, + 5, + &hints), c); + } else { + Q_XCB_CALL2(xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS)), c); + } +} + +void QXcbWindow::printNetWmState(const QVector<xcb_atom_t> &state) +{ + printf("_NET_WM_STATE (%d): ", state.size()); + for (int i = 0; i < state.size(); ++i) { +#define CHECK_WM_STATE(state_atom) \ + if (state.at(i) == atom(QXcbAtom::state_atom))\ + printf(#state_atom " "); + CHECK_WM_STATE(_NET_WM_STATE_ABOVE) + CHECK_WM_STATE(_NET_WM_STATE_BELOW) + CHECK_WM_STATE(_NET_WM_STATE_FULLSCREEN) + CHECK_WM_STATE(_NET_WM_STATE_MAXIMIZED_HORZ) + CHECK_WM_STATE(_NET_WM_STATE_MAXIMIZED_VERT) + CHECK_WM_STATE(_NET_WM_STATE_MODAL) + CHECK_WM_STATE(_NET_WM_STATE_STAYS_ON_TOP) + CHECK_WM_STATE(_NET_WM_STATE_DEMANDS_ATTENTION) +#undef CHECK_WM_STATE + } + printf("\n"); +} + +QVector<xcb_atom_t> QXcbWindow::getNetWmState() +{ + QVector<xcb_atom_t> result; + + xcb_get_property_cookie_t get_cookie = + xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE), + XCB_ATOM_ATOM, 0, 1024); + + xcb_generic_error_t *error; + + xcb_get_property_reply_t *reply = + xcb_get_property_reply(xcb_connection(), get_cookie, &error); + + if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) { + result.resize(reply->length); + + memcpy(result.data(), xcb_get_property_value(reply), reply->length * sizeof(xcb_atom_t)); + +#ifdef NET_WM_STATE_DEBUG + printf("getting net wm state (%x)\n", m_window); + printNetWmState(result); +#endif + + free(reply); + } else if (error) { + connection()->handleXcbError(error); + free(error); + } else { +#ifdef NET_WM_STATE_DEBUG + printf("getting net wm state (%x), empty\n", m_window); +#endif + } + + return result; +} + +void QXcbWindow::setNetWmState(const QVector<xcb_atom_t> &atoms) +{ + if (atoms.isEmpty()) { + Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE))); + } else { + Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32, + atoms.count(), atoms.constData())); + } + xcb_flush(xcb_connection()); +} + + Qt::WindowFlags QXcbWindow::setWindowFlags(Qt::WindowFlags flags) { Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); - setNetWmWindowTypes(flags); - if (type == Qt::ToolTip) flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint; if (type == Qt::Popup) flags |= Qt::X11BypassWindowManagerHint; - bool topLevel = (flags & Qt::Window); - bool popup = (type == Qt::Popup); - bool dialog = (type == Qt::Dialog - || type == Qt::Sheet); - bool desktop = (type == Qt::Desktop); - bool tool = (type == Qt::Tool || type == Qt::SplashScreen - || type == Qt::ToolTip || type == Qt::Drawer); + setNetWmWindowFlags(flags); + setMotifWindowFlags(flags); - Q_UNUSED(topLevel); - Q_UNUSED(dialog); - Q_UNUSED(desktop); - Q_UNUSED(tool); + return flags; +} - bool tooltip = (type == Qt::ToolTip); +void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) +{ + Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); - QtMWMHints mwmhints; + QtMotifWmHints mwmhints; mwmhints.flags = 0L; mwmhints.functions = 0L; mwmhints.decorations = 0; @@ -403,30 +723,90 @@ Qt::WindowFlags QXcbWindow::setWindowFlags(Qt::WindowFlags flags) mwmhints.decorations = 0; } - if (mwmhints.flags != 0l) { - Q_XCB_CALL(xcb_change_property(xcb_connection(), - XCB_PROP_MODE_REPLACE, - m_window, - atom(QXcbAtom::_MOTIF_WM_HINTS), - atom(QXcbAtom::_MOTIF_WM_HINTS), - 32, - 5, - &mwmhints)); - } else { - Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS))); - } + setMotifWmHints(connection(), m_window, mwmhints); +} + +void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two) +{ + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = m_window; + event.type = atom(QXcbAtom::_NET_WM_STATE); + event.data.data32[0] = set ? 1 : 0; + event.data.data32[1] = one; + event.data.data32[2] = two; + event.data.data32[3] = 0; + event.data.data32[4] = 0; + + Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); +} - if (popup || tooltip) { - const quint32 mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER; - const quint32 values[] = { true, true }; +Qt::WindowState QXcbWindow::setWindowState(Qt::WindowState state) +{ + if (state == m_windowState) + return state; + + m_dirtyFrameMargins = true; + + // unset old state + switch (m_windowState) { + case Qt::WindowMinimized: + Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); + break; + case Qt::WindowMaximized: + changeNetWmState(false, + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + break; + case Qt::WindowFullScreen: + changeNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + break; + default: + break; + } - Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values)); + // set new state + switch (state) { + case Qt::WindowMinimized: + { + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = m_window; + event.type = atom(QXcbAtom::WM_CHANGE_STATE); + event.data.data32[0] = XCB_WM_STATE_ICONIC; + event.data.data32[1] = 0; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = 0; + + Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event)); + } + break; + case Qt::WindowMaximized: + changeNetWmState(true, + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + break; + case Qt::WindowFullScreen: + changeNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + break; + case Qt::WindowNoState: + break; + default: + break; } - return QPlatformWindow::setWindowFlags(flags); + connection()->sync(); + + m_windowState = state; + return m_windowState; } -void QXcbWindow::setNetWmWindowTypes(Qt::WindowFlags flags) +void QXcbWindow::setNetWmWindowFlags(Qt::WindowFlags flags) { // in order of decreasing priority QVector<uint> windowTypes; @@ -462,6 +842,127 @@ void QXcbWindow::setNetWmWindowTypes(Qt::WindowFlags flags) windowTypes.count(), windowTypes.constData())); } +void QXcbWindow::updateMotifWmHintsBeforeMap() +{ + QtMotifWmHints mwmhints = getMotifWmHints(connection(), m_window); + + if (window()->windowModality() != Qt::NonModal) { + switch (window()->windowModality()) { + case Qt::WindowModal: + mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL; + break; + case Qt::ApplicationModal: + default: + mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL; + break; + } + mwmhints.flags |= MWM_HINTS_INPUT_MODE; + } else { + mwmhints.input_mode = MWM_INPUT_MODELESS; + mwmhints.flags &= ~MWM_HINTS_INPUT_MODE; + } + + if (window()->minimumSize() == window()->maximumSize()) { + // fixed size, remove the resize handle (since mwm/dtwm + // isn't smart enough to do it itself) + mwmhints.flags |= MWM_HINTS_FUNCTIONS; + if (mwmhints.functions == MWM_FUNC_ALL) { + mwmhints.functions = MWM_FUNC_MOVE; + } else { + mwmhints.functions &= ~MWM_FUNC_RESIZE; + } + + if (mwmhints.decorations == MWM_DECOR_ALL) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + mwmhints.decorations = (MWM_DECOR_BORDER + | MWM_DECOR_TITLE + | MWM_DECOR_MENU); + } else { + mwmhints.decorations &= ~MWM_DECOR_RESIZEH; + } + } + + if (window()->windowFlags() & Qt::WindowMinimizeButtonHint) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + mwmhints.decorations |= MWM_DECOR_MINIMIZE; + mwmhints.functions |= MWM_FUNC_MINIMIZE; + } + if (window()->windowFlags() & Qt::WindowMaximizeButtonHint) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + mwmhints.decorations |= MWM_DECOR_MAXIMIZE; + mwmhints.functions |= MWM_FUNC_MAXIMIZE; + } + if (window()->windowFlags() & Qt::WindowCloseButtonHint) + mwmhints.functions |= MWM_FUNC_CLOSE; + + setMotifWmHints(connection(), m_window, mwmhints); +} + +void QXcbWindow::updateNetWmStateBeforeMap() +{ + QVector<xcb_atom_t> netWmState; + + Qt::WindowFlags flags = window()->windowFlags(); + if (flags & Qt::WindowStaysOnTopHint) { + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_ABOVE)); + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)); + } else if (flags & Qt::WindowStaysOnBottomHint) { + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_BELOW)); + } + + if (window()->windowState() & Qt::WindowFullScreen) { + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + } + + if (window()->windowState() & Qt::WindowMaximized) { + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)); + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + } + + if (window()->windowModality() != Qt::NonModal) { + netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MODAL)); + } + + setNetWmState(netWmState); +} + +void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) +{ + xcb_window_t wid = m_window; + + const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); + if (m_netWmUserTimeWindow || isSupportedByWM) { + if (!m_netWmUserTimeWindow) { + m_netWmUserTimeWindow = xcb_generate_id(xcb_connection()); + Q_XCB_CALL(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, // depth -- same as root + m_netWmUserTimeWindow, // window id + m_window, // parent window id + -1, -1, 1, 1, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + m_screen->screen()->root_visual, // visual + 0, // value mask + 0)); // value list + wid = m_netWmUserTimeWindow; + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW), + XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME)); + } else if (!isSupportedByWM) { + // WM no longer supports it, then we should remove the + // _NET_WM_USER_TIME_WINDOW atom. + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); + xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow); + m_netWmUserTimeWindow = XCB_NONE; + } else { + wid = m_netWmUserTimeWindow; + } + } + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::_NET_WM_USER_TIME), + XCB_ATOM_CARDINAL, 32, 1, ×tamp); +} + + WId QXcbWindow::winId() const { return m_window; @@ -469,8 +970,13 @@ WId QXcbWindow::winId() const void QXcbWindow::setParent(const QPlatformWindow *parent) { + // re-create for compatibility + create(); + QPoint topLeft = geometry().topLeft(); - Q_XCB_CALL(xcb_reparent_window(xcb_connection(), window(), static_cast<const QXcbWindow *>(parent)->window(), topLeft.x(), topLeft.y())); + + xcb_window_t xcb_parent_id = parent ? static_cast<const QXcbWindow *>(parent)->xcb_window() : m_screen->root(); + Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y())); } void QXcbWindow::setWindowTitle(const QString &title) @@ -500,56 +1006,87 @@ void QXcbWindow::lower() Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values)); } +void QXcbWindow::propagateSizeHints() +{ + // update WM_NORMAL_HINTS + xcb_size_hints_t hints; + memset(&hints, 0, sizeof(hints)); + + QRect rect = geometry(); + + xcb_size_hints_set_position(&hints, true, rect.x(), rect.y()); + xcb_size_hints_set_size(&hints, true, rect.width(), rect.height()); + xcb_size_hints_set_win_gravity(&hints, XCB_GRAVITY_STATIC); + + QWindow *win = window(); + + QSize minimumSize = win->minimumSize(); + QSize maximumSize = win->maximumSize(); + QSize baseSize = win->baseSize(); + QSize sizeIncrement = win->sizeIncrement(); + + if (minimumSize.width() > 0 || minimumSize.height() > 0) + xcb_size_hints_set_min_size(&hints, minimumSize.width(), minimumSize.height()); + + if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX) + xcb_size_hints_set_max_size(&hints, + qMin(XCOORD_MAX, maximumSize.width()), + qMin(XCOORD_MAX, maximumSize.height())); + + if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) { + xcb_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); + xcb_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height()); + } + + xcb_set_wm_normal_hints(xcb_connection(), m_window, &hints); +} + void QXcbWindow::requestActivateWindow() { - Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, XCB_TIME_CURRENT_TIME)); + if (m_mapped){ + updateNetWmUserTime(connection()->time()); + Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time())); + } connection()->sync(); } -QPlatformGLContext *QXcbWindow::glContext() const +QSurfaceFormat QXcbWindow::format() const { - if (!QApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { - printf("no opengl\n"); - return 0; - } - if (!m_context) { -#if defined(XCB_USE_GLX) - QXcbWindow *that = const_cast<QXcbWindow *>(this); - that->m_context = new QGLXContext(m_window, m_screen, widget()->platformWindowFormat()); -#elif defined(XCB_USE_EGL) + // ### return actual format + return m_requestedFormat; +} + +#if defined(XCB_USE_EGL) +QXcbEGLSurface *QXcbWindow::eglSurface() const +{ + if (!m_eglSurface) { EGLDisplay display = connection()->egl_display(); - EGLConfig config = q_configFromQPlatformWindowFormat(display,widget()->platformWindowFormat(),true); - QVector<EGLint> eglContextAttrs; - eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION); - eglContextAttrs.append(2); - eglContextAttrs.append(EGL_NONE); - - EGLSurface eglSurface = eglCreateWindowSurface(display,config,(EGLNativeWindowType)m_window,0); - QXcbWindow *that = const_cast<QXcbWindow *>(this); - that->m_context = new QEGLPlatformContext(display, config, eglContextAttrs.data(), eglSurface, EGL_OPENGL_ES_API); -#elif defined(XCB_USE_DRI2) - QXcbWindow *that = const_cast<QXcbWindow *>(this); - that->m_context = new QDri2Context(that); -#endif + EGLConfig config = q_configFromGLFormat(display, window()->format(), true); + EGLSurface surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)m_window, 0); + + m_eglSurface = new QXcbEGLSurface(display, surface); } - return m_context; + + return m_eglSurface; } +#endif void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) { - QWindowSurface *surface = widget()->windowSurface(); - if (surface) { - QRect rect(event->x, event->y, event->width, event->height); - - surface->flush(widget(), rect, QPoint()); - } + QRect rect(event->x, event->y, event->width, event->height); + QWindowSystemInterface::handleSynchronousExposeEvent(window(), rect); } void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event) { - if (event->format == 32 && event->type == atom(QXcbAtom::WM_PROTOCOLS)) { + if (event->format != 32) + return; + + if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) { if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) { - QWindowSystemInterface::handleCloseEvent(widget()); + QWindowSystemInterface::handleCloseEvent(window()); + } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) { + connection()->setTime(event->data.data32[1]); } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) { xcb_client_message_event_t reply = *event; @@ -559,13 +1096,22 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply); xcb_flush(xcb_connection()); } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { - if (!m_hasReceivedSyncRequest) { - m_hasReceivedSyncRequest = true; - printf("Window manager supports _NET_WM_SYNC_REQUEST, syncing resizes\n"); - } + connection()->setTime(event->data.data32[1]); m_syncValue.lo = event->data.data32[2]; m_syncValue.hi = event->data.data32[3]; + } else { + qWarning() << "unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]); } + } else if (event->type == atom(QXcbAtom::XdndEnter)) { + connection()->drag()->handleEnter(window(), event); + } else if (event->type == atom(QXcbAtom::XdndPosition)) { + connection()->drag()->handlePosition(window(), event, false); + } else if (event->type == atom(QXcbAtom::XdndLeave)) { + connection()->drag()->handleLeave(window(), event, false); + } else if (event->type == atom(QXcbAtom::XdndDrop)) { + connection()->drag()->handleDrop(window(), event, false); + } else { + qWarning() << "unhandled client message:" << connection()->atomName(event->type); } } @@ -581,11 +1127,8 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * QRect rect(xpos, ypos, event->width, event->height); - if (rect == geometry()) - return; - QPlatformWindow::setGeometry(rect); - QWindowSystemInterface::handleGeometryChange(widget(), rect); + QWindowSystemInterface::handleGeometryChange(window(), rect); #if XCB_USE_DRI2 if (m_context) @@ -593,6 +1136,22 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * #endif } +void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) +{ + if (event->window == m_window) { + m_mapped = true; + QWindowSystemInterface::handleMapEvent(window()); + } +} + +void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) +{ + if (event->window == m_window) { + m_mapped = false; + QWindowSystemInterface::handleUnmapEvent(window()); + } +} + static Qt::MouseButtons translateMouseButtons(int s) { Qt::MouseButtons ret = 0; @@ -621,6 +1180,8 @@ static Qt::MouseButton translateMouseButton(xcb_button_t s) void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) { + updateNetWmUserTime(event->time); + QPoint local(event->event_x, event->event_y); QPoint global(event->root_x, event->root_y); @@ -633,7 +1194,7 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) && (modifiers & Qt::AltModifier)) || (event->detail == 6 || event->detail == 7)); - QWindowSystemInterface::handleWheelEvent(widget(), event->time, + QWindowSystemInterface::handleWheelEvent(window(), event->time, local, global, delta, hor ? Qt::Horizontal : Qt::Vertical); return; } @@ -659,32 +1220,65 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event) void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global) { + connection()->setTime(time); + Qt::MouseButtons buttons = translateMouseButtons(state); Qt::MouseButton button = translateMouseButton(detail); buttons ^= button; // X event uses state *before*, Qt uses state *after* - QWindowSystemInterface::handleMouseEvent(widget(), time, local, global, buttons); + QWindowSystemInterface::handleMouseEvent(window(), time, local, global, buttons); } -void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *) +void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) { - QWindowSystemInterface::handleEnterEvent(widget()); + connection()->setTime(event->time); + + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) + || event->detail == XCB_NOTIFY_DETAIL_VIRTUAL + || event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL) + { + return; + } + + QWindowSystemInterface::handleEnterEvent(window()); } -void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *) +void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) { - QWindowSystemInterface::handleLeaveEvent(widget()); + connection()->setTime(event->time); + + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) + || event->detail == XCB_NOTIFY_DETAIL_INFERIOR) + { + return; + } + + QWindowSystemInterface::handleLeaveEvent(window()); } void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *) { - QWindowSystemInterface::handleWindowActivated(widget()); + QWindowSystemInterface::handleWindowActivated(window()); +} + +static bool focusInPeeker(xcb_generic_event_t *event) +{ + if (!event) { + // FocusIn event is not in the queue, proceed with FocusOut normally. + QWindowSystemInterface::handleWindowActivated(0); + return true; + } + uint response_type = event->response_type & ~0x80; + return response_type == XCB_FOCUS_IN; } void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) { - QWindowSystemInterface::handleWindowActivated(0); + // Do not set the active window to 0 if there is a FocusIn coming. + // There is however no equivalent for XPutBackEvent so register a + // callback for QXcbConnection instead. + connection()->addPeekFunc(focusInPeeker); } void QXcbWindow::updateSyncRequestCounter() @@ -698,3 +1292,47 @@ void QXcbWindow::updateSyncRequestCounter() m_syncValue.hi = 0; } } + +bool QXcbWindow::setKeyboardGrabEnabled(bool grab) +{ + if (!grab) { + xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME); + return true; + } + xcb_grab_keyboard_cookie_t cookie = xcb_grab_keyboard(xcb_connection(), false, + m_window, XCB_TIME_CURRENT_TIME, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + xcb_generic_error_t *err; + xcb_grab_keyboard_reply_t *reply = xcb_grab_keyboard_reply(xcb_connection(), cookie, &err); + bool result = !(err || !reply || reply->status != XCB_GRAB_STATUS_SUCCESS); + free(reply); + free(err); + return result; +} + +bool QXcbWindow::setMouseGrabEnabled(bool grab) +{ + if (!grab) { + xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME); + return true; + } + xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer(xcb_connection(), false, m_window, + (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE + | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW + | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION), + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, + XCB_WINDOW_NONE, XCB_CURSOR_NONE, + XCB_TIME_CURRENT_TIME); + xcb_generic_error_t *err; + xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, &err); + bool result = !(err || !reply || reply->status != XCB_GRAB_STATUS_SUCCESS); + free(reply); + free(err); + return result; +} + +void QXcbWindow::setCursor(xcb_cursor_t cursor) +{ + xcb_change_window_attributes(xcb_connection(), m_window, XCB_CW_CURSOR, &cursor); + xcb_flush(xcb_connection()); +} diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 69d0bc2f1d..a85760ea8d 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -43,7 +43,7 @@ #define QXCBWINDOW_H #include <QtGui/QPlatformWindow> -#include <QtGui/QPlatformWindowFormat> +#include <QtGui/QSurfaceFormat> #include <QtGui/QImage> #include <xcb/xcb.h> @@ -52,35 +52,47 @@ #include "qxcbobject.h" class QXcbScreen; +class QXcbEGLSurface; class QXcbWindow : public QXcbObject, public QPlatformWindow { public: - QXcbWindow(QWidget *tlw); + QXcbWindow(QWindow *window); ~QXcbWindow(); void setGeometry(const QRect &rect); + QMargins frameMargins() const; + void setVisible(bool visible); Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags); + Qt::WindowState setWindowState(Qt::WindowState state); WId winId() const; void setParent(const QPlatformWindow *window); void setWindowTitle(const QString &title); void raise(); void lower(); + void propagateSizeHints(); void requestActivateWindow(); - QPlatformGLContext *glContext() const; + bool setKeyboardGrabEnabled(bool grab); + bool setMouseGrabEnabled(bool grab); + + void setCursor(xcb_cursor_t cursor); - xcb_window_t window() const { return m_window; } + QSurfaceFormat format() const; + + xcb_window_t xcb_window() const { return m_window; } uint depth() const { return m_depth; } - QImage::Format format() const { return m_format; } + QImage::Format imageFormat() const { return m_imageFormat; } void handleExposeEvent(const xcb_expose_event_t *event); void handleClientMessageEvent(const xcb_client_message_event_t *event); void handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event); + void handleMapNotifyEvent(const xcb_map_notify_event_t *event); + void handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event); void handleButtonPressEvent(const xcb_button_press_event_t *event); void handleButtonReleaseEvent(const xcb_button_release_event_t *event); void handleMotionNotifyEvent(const xcb_motion_notify_event_t *event); @@ -93,22 +105,54 @@ public: void handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global); void updateSyncRequestCounter(); + void updateNetWmUserTime(xcb_timestamp_t timestamp); + void netWmUserTime() const; + +#if defined(XCB_USE_EGL) + QXcbEGLSurface *eglSurface() const; +#endif private: - void setNetWmWindowTypes(Qt::WindowFlags flags); + void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0); + QVector<xcb_atom_t> getNetWmState(); + void setNetWmState(const QVector<xcb_atom_t> &atoms); + void printNetWmState(const QVector<xcb_atom_t> &state); + + void setNetWmWindowFlags(Qt::WindowFlags flags); + void setMotifWindowFlags(Qt::WindowFlags flags); + + void updateMotifWmHintsBeforeMap(); + void updateNetWmStateBeforeMap(); + + void create(); + void destroy(); + + void show(); + void hide(); QXcbScreen *m_screen; xcb_window_t m_window; - QPlatformGLContext *m_context; uint m_depth; - QImage::Format m_format; + QImage::Format m_imageFormat; xcb_sync_int64_t m_syncValue; xcb_sync_counter_t m_syncCounter; - bool m_hasReceivedSyncRequest; + Qt::WindowState m_windowState; + + bool m_mapped; + xcb_window_t m_netWmUserTimeWindow; + + QSurfaceFormat m_requestedFormat; + + mutable bool m_dirtyFrameMargins; + mutable QMargins m_frameMargins; + +#if defined(XCB_USE_EGL) + mutable QXcbEGLSurface *m_eglSurface; +#endif }; #endif diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp new file mode 100644 index 0000000000..5fb67b6377 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbwmsupport.h" +#include "qxcbscreen.h" + +#include <qdebug.h> + +QXcbWMSupport::QXcbWMSupport(QXcbConnection *c) + : QXcbObject(c) +{ + updateNetWMAtoms(); + updateVirtualRoots(); +} + +bool QXcbWMSupport::isSupportedByWM(xcb_atom_t atom) const +{ + return net_wm_atoms.contains(atom); +} + + + +void QXcbWMSupport::updateNetWMAtoms() +{ + net_wm_atoms.clear(); + + xcb_window_t root = connection()->screens().at(connection()->primaryScreen())->root(); + int offset = 0; + int remaining = 0; + do { + xcb_generic_error_t *error = 0; + xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024); + xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, &error); + if (!reply || error) + break; + + remaining = 0; + + if (reply->type == XCB_ATOM_ATOM && reply->format == 32) { + int len = xcb_get_property_value_length(reply)/4; + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + int s = net_wm_atoms.size(); + net_wm_atoms.resize(s + len); + memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t)); + + remaining = reply->bytes_after; + offset += len; + } + + free(reply); + } while (remaining > 0); + +// qDebug() << "======== updateNetWMAtoms"; +// for (int i = 0; i < net_wm_atoms.size(); ++i) +// qDebug() << atomName(net_wm_atoms.at(i)); +// qDebug() << "======== updateNetWMAtoms"; +} + +// update the virtual roots array +void QXcbWMSupport::updateVirtualRoots() +{ + net_virtual_roots.clear(); + + if (!isSupportedByWM(atom(QXcbAtom::_NET_VIRTUAL_ROOTS))) + return; + + xcb_window_t root = connection()->screens().at(connection()->primaryScreen())->root(); + int offset = 0; + int remaining = 0; + do { + xcb_generic_error_t *error = 0; + 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_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, &error); + if (!reply || error) + break; + + remaining = 0; + + if (reply->type == XCB_ATOM_ATOM && reply->format == 32) { + int len = xcb_get_property_value_length(reply)/4; + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + int s = net_wm_atoms.size(); + net_wm_atoms.resize(s + len); + memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t)); + + remaining = reply->bytes_after; + offset += len; + } + + free(reply); + } while (remaining > 0); + + qDebug() << "======== updateVirtualRoots"; + for (int i = 0; i < net_virtual_roots.size(); ++i) + qDebug() << connection()->atomName(net_virtual_roots.at(i)); + qDebug() << "======== updateVirtualRoots"; +} + diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.h b/src/plugins/platforms/xcb/qxcbwmsupport.h new file mode 100644 index 0000000000..3b02ef3e7e --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbwmsupport.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QXCBWMSUPPORT_H +#define QXCBWMSUPPORT_H + +#include "qxcbobject.h" +#include "qxcbconnection.h" +#include <qvector.h> + +class QXcbWMSupport : public QXcbObject +{ +public: + QXcbWMSupport(QXcbConnection *c); + + + bool isSupportedByWM(xcb_atom_t atom) const; + const QVector<xcb_window_t> &virtualRoots() const { return net_virtual_roots; } + +private: + friend class QXcbConnection; + void updateNetWMAtoms(); + void updateVirtualRoots(); + + QVector<xcb_atom_t> net_wm_atoms; + QVector<xcb_window_t> net_virtual_roots; +}; + + +#endif diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro index 27d10b6756..143a671047 100644 --- a/src/plugins/platforms/xcb/xcb.pro +++ b/src/plugins/platforms/xcb/xcb.pro @@ -3,70 +3,91 @@ TARGET = xcb load(qt_plugin) QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms -QT += core-private gui-private +QT += core-private gui-private platformsupport-private SOURCES = \ + qxcbclipboard.cpp \ qxcbconnection.cpp \ qxcbintegration.cpp \ qxcbkeyboard.cpp \ + qxcbmime.cpp \ + qxcbdrag.cpp \ qxcbscreen.cpp \ qxcbwindow.cpp \ - qxcbwindowsurface.cpp \ + qxcbbackingstore.cpp \ + qxcbwmsupport.cpp \ main.cpp \ - qxcbnativeinterface.cpp + qxcbnativeinterface.cpp \ + qxcbcursor.cpp \ + qxcbimage.cpp HEADERS = \ + qxcbclipboard.h \ qxcbconnection.h \ qxcbintegration.h \ qxcbkeyboard.h \ + qxcbdrag.h \ + qxcbmime.h \ qxcbobject.h \ qxcbscreen.h \ qxcbwindow.h \ - qxcbwindowsurface.h \ - qxcbnativeinterface.h + qxcbbackingstore.h \ + qxcbwmsupport.h \ + qxcbnativeinterface.h \ + qxcbcursor.h \ + qxcbimage.h -contains(QT_CONFIG, opengl) { - QT += opengl +contains(QT_CONFIG, xcb-poll-for-queued-event) { + DEFINES += XCB_POLL_FOR_QUEUED_EVENT +} + +# needed by GLX, Xcursor, XLookupString, ... +contains(QT_CONFIG, xcb-xlib) { + DEFINES += XCB_USE_XLIB + LIBS += -lX11 -lX11-xcb +} + +# to support custom cursors with depth > 1 +contains(QT_CONFIG, xcb-render) { + DEFINES += XCB_USE_RENDER + LIBS += -lxcb-render -lxcb-render-util -lXrender +} # DEFINES += XCB_USE_DRI2 - contains(DEFINES, XCB_USE_DRI2) { - LIBS += -lxcb-dri2 -lxcb-xfixes -lEGL - - CONFIG += link_pkgconfig - PKGCONFIG += libdrm - - HEADERS += qdri2context.h - SOURCES += qdri2context.cpp - - } else { - DEFINES += XCB_USE_XLIB - LIBS += -lX11 -lX11-xcb - - contains(QT_CONFIG, opengles2) { - DEFINES += XCB_USE_EGL - HEADERS += \ - ../eglconvenience/qeglplatformcontext.h \ - ../eglconvenience/qeglconvenience.h \ - ../eglconvenience/qxlibeglintegration.h - - SOURCES += \ - ../eglconvenience/qeglplatformcontext.cpp \ - ../eglconvenience/qeglconvenience.cpp \ - ../eglconvenience/qxlibeglintegration.cpp - - LIBS += -lEGL - } else { - DEFINES += XCB_USE_GLX - include (../glxconvenience/glxconvenience.pri) - HEADERS += qglxintegration.h - SOURCES += qglxintegration.cpp - } +contains(DEFINES, XCB_USE_DRI2) { + LIBS += -lxcb-dri2 -lEGL + + CONFIG += link_pkgconfig + PKGCONFIG += libdrm + + HEADERS += qdri2context.h + SOURCES += qdri2context.cpp + +} else { + contains(QT_CONFIG, opengles2) { + DEFINES += XCB_USE_EGL + LIBS += -lEGL + HEADERS += qxcbeglsurface.h + } else:contains(QT_CONFIG, xcb-xlib) { + DEFINES += XCB_USE_GLX + HEADERS += qglxintegration.h + SOURCES += qglxintegration.cpp } } -LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync +LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes + +DEFINES += $$QMAKE_DEFINES_XCB +LIBS += $$QMAKE_LIBS_XCB +QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB -include (../fontdatabases/genericunix/genericunix.pri) +CONFIG += qpa/genericunixfontdatabase + +contains(QT_CONFIG, dbus) { +DEFINES += XCB_USE_IBUS +QT += dbus +LIBS += -ldbus-1 +} target.path += $$[QT_INSTALL_PLUGINS]/platforms INSTALLS += target |