Fixed non-GL applications crashing when GLX/EGL initialization fails on xcb.
[profile/ivi/qtbase.git] / src / plugins / platforms / xcb / qxcbwindow.cpp
index 6996c87..7020e63 100644 (file)
 
 #include <QtDebug>
 #include <QScreen>
+#include <QtGui/QIcon>
+#include <QtGui/QRegion>
 
 #include "qxcbconnection.h"
 #include "qxcbscreen.h"
 #include "qxcbdrag.h"
 #include "qxcbkeyboard.h"
 #include "qxcbwmsupport.h"
+#include "qxcbimage.h"
 
-#include <qplatformintegration_qpa.h>
+#include <qpa/qplatformintegration.h>
 
 #ifdef XCB_USE_DRI2
 #include "qdri2context.h"
@@ -61,6 +64,9 @@
 #include <xcb/xcb_icccm.h>
 #undef class
 #include <xcb/xfixes.h>
+#ifndef QT_NO_SHAPE
+#  include <xcb/shape.h>
+#endif // QT_NO_SHAPE
 
 // xcb-icccm 3.8 support
 #ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
@@ -87,8 +93,8 @@
 #include <private/qguiapplication_p.h>
 #include <private/qwindow_p.h>
 
-#include <QtGui/QPlatformBackingStore>
-#include <QtGui/QWindowSystemInterface>
+#include <qpa/qplatformbackingstore.h>
+#include <qpa/qwindowsysteminterface.h>
 
 #include <stdio.h>
 
 #include <X11/Xutil.h>
 #endif
 
-#ifdef XCB_USE_XINPUT2_MAEMO
+#if defined(XCB_USE_XINPUT2_MAEMO) || defined(XCB_USE_XINPUT2)
 #include <X11/extensions/XInput2.h>
 #endif
 
@@ -138,14 +144,21 @@ static inline QImage::Format imageFormatForDepth(int depth)
     }
 }
 
+static inline bool positionIncludesFrame(QWindow *w)
+{
+    return qt_window_private(w)->positionPolicy == QWindowPrivate::WindowFrameInclusive;
+}
+
 QXcbWindow::QXcbWindow(QWindow *window)
     : QPlatformWindow(window)
     , m_window(0)
     , m_syncCounter(0)
+    , m_gravity(XCB_GRAVITY_STATIC)
     , m_mapped(false)
     , m_transparent(false)
     , m_deferredActivation(false)
     , m_netWmUserTimeWindow(XCB_NONE)
+    , m_dirtyFrameMargins(false)
 #if defined(XCB_USE_EGL)
     , m_eglSurface(0)
 #endif
@@ -176,6 +189,11 @@ void QXcbWindow::create()
         return;
     }
 
+    // Determine gravity from initial position. Do not change
+    // later as it will cause the window to move uncontrollably.
+    m_gravity = positionIncludesFrame(window()) ?
+                XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
+
     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
@@ -199,6 +217,9 @@ void QXcbWindow::create()
         | XCB_EVENT_MASK_FOCUS_CHANGE
     };
 
+    // Parameters to XCreateWindow() are frame corner + inner size.
+    // This fits in case position policy is frame inclusive. There is
+    // currently no way to implement it for frame-exclusive geometries.
     QRect rect = window()->geometry();
     QPlatformWindow::setGeometry(rect);
 
@@ -217,7 +238,7 @@ void QXcbWindow::create()
     {
 #if defined(XCB_USE_GLX)
         XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), &m_format);
-        if (!visualInfo)
+        if (!visualInfo && window()->surfaceType() == QSurface::OpenGLSurface)
             qFatal("Could not initialize GLX");
 #elif defined(XCB_USE_EGL)
         EGLDisplay eglDisplay = connection()->egl_display();
@@ -233,26 +254,30 @@ void QXcbWindow::create()
         XVisualInfo *visualInfo;
         int matchingCount = 0;
         visualInfo = XGetVisualInfo(DISPLAY_FROM_XCB(this), VisualIDMask, &visualInfoTemplate, &matchingCount);
-        if (!visualInfo)
+        if (!visualInfo && window()->surfaceType() == QSurface::OpenGLSurface)
             qFatal("Could not initialize EGL");
 #endif //XCB_USE_GLX
-        m_depth = visualInfo->depth;
-        m_imageFormat = imageFormatForDepth(m_depth);
-        Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), xcb_parent_id, visualInfo->visual, AllocNone);
+        if (visualInfo) {
+            m_depth = visualInfo->depth;
+            m_imageFormat = imageFormatForDepth(m_depth);
+            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;
+            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_visualId = visualInfo->visualid;
+            m_visualId = visualInfo->visualid;
 
-        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);
+            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);
 
-        XFree(visualInfo);
-    } else
+            XFree(visualInfo);
+        }
+    }
+
+    if (!m_window)
 #endif //defined(XCB_USE_GLX) || defined(XCB_USE_EGL)
     {
         m_window = xcb_generate_id(xcb_connection());
@@ -338,7 +363,7 @@ void QXcbWindow::create()
                                    1, &leader));
 
 #ifdef XCB_USE_XINPUT2_MAEMO
-    if (connection()->isUsingXInput2()) {
+    if (connection()->isUsingXInput2Maemo()) {
         XIEventMask xieventmask;
         uchar bitmask[2] = { 0, 0 };
 
@@ -352,6 +377,8 @@ void QXcbWindow::create()
 
         XISelectEvents(DISPLAY_FROM_XCB(this), m_window, &xieventmask, 1);
     }
+#elif defined(XCB_USE_XINPUT2)
+    connection()->xi2Select(m_window);
 #endif
 
     setWindowFlags(window()->windowFlags());
@@ -361,7 +388,9 @@ void QXcbWindow::create()
     if (window()->windowFlags() & Qt::WindowTransparentForInput)
         setTransparentForMouseEvents(true);
 
+#ifndef QT_NO_DRAGANDDROP
     connection()->drag()->dndEnable(this, true);
+#endif
 }
 
 QXcbWindow::~QXcbWindow()
@@ -384,6 +413,7 @@ void QXcbWindow::destroy()
         }
         connection()->removeWindow(m_window);
         Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window));
+        m_window = 0;
     }
     m_mapped = false;
 
@@ -398,13 +428,14 @@ void QXcbWindow::setGeometry(const QRect &rect)
     QPlatformWindow::setGeometry(rect);
 
     propagateSizeHints();
+    const QRect wmGeometry = windowToWmGeometry(rect);
 
     const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
     const qint32 values[] = {
-        qBound<qint32>(-XCOORD_MAX, rect.x(),      XCOORD_MAX),
-        qBound<qint32>(-XCOORD_MAX, rect.y(),      XCOORD_MAX),
-        qBound<qint32>(1,           rect.width(),  XCOORD_MAX),
-        qBound<qint32>(1,           rect.height(), XCOORD_MAX),
+        qBound<qint32>(-XCOORD_MAX, wmGeometry.x(),      XCOORD_MAX),
+        qBound<qint32>(-XCOORD_MAX, wmGeometry.y(),      XCOORD_MAX),
+        qBound<qint32>(1,           wmGeometry.width(),  XCOORD_MAX),
+        qBound<qint32>(1,           wmGeometry.height(), XCOORD_MAX),
     };
 
     Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
@@ -1104,6 +1135,49 @@ void QXcbWindow::setWindowTitle(const QString &title)
                                    ba.constData()));
 }
 
+void QXcbWindow::setWindowIcon(const QIcon &icon)
+{
+    QVector<quint32> icon_data;
+
+    if (!icon.isNull()) {
+        QList<QSize> availableSizes = icon.availableSizes();
+        if (availableSizes.isEmpty()) {
+            // try to use default sizes since the icon can be a scalable image like svg.
+            availableSizes.push_back(QSize(16,16));
+            availableSizes.push_back(QSize(32,32));
+            availableSizes.push_back(QSize(64,64));
+            availableSizes.push_back(QSize(128,128));
+        }
+        for (int i = 0; i < availableSizes.size(); ++i) {
+            QSize size = availableSizes.at(i);
+            QPixmap pixmap = icon.pixmap(size);
+            if (!pixmap.isNull()) {
+                QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+                int pos = icon_data.size();
+                icon_data.resize(pos + 2 + image.width()*image.height());
+                icon_data[pos++] = image.width();
+                icon_data[pos++] = image.height();
+                memcpy(icon_data.data() + pos, image.bits(), image.width()*image.height()*4);
+            }
+        }
+    }
+
+    if (!icon_data.isEmpty()) {
+        Q_XCB_CALL(xcb_change_property(xcb_connection(),
+                                       XCB_PROP_MODE_REPLACE,
+                                       m_window,
+                                       atom(QXcbAtom::_NET_WM_ICON),
+                                       atom(QXcbAtom::CARDINAL),
+                                       32,
+                                       icon_data.size(),
+                                       (unsigned char *) icon_data.data()));
+    } else {
+        Q_XCB_CALL(xcb_delete_property(xcb_connection(),
+                                       m_window,
+                                       atom(QXcbAtom::_NET_WM_ICON)));
+    }
+}
+
 void QXcbWindow::raise()
 {
     const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
@@ -1118,20 +1192,36 @@ void QXcbWindow::lower()
     Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
 }
 
+// Adapt the geometry to match the WM expection with regards
+// to gravity.
+QRect QXcbWindow::windowToWmGeometry(QRect r) const
+{
+    if (m_dirtyFrameMargins || m_frameMargins.isNull())
+        return r;
+    const bool frameInclusive = positionIncludesFrame(window());
+    // XCB_GRAVITY_STATIC requires the inner geometry, whereas
+    // XCB_GRAVITY_NORTH_WEST requires the frame geometry
+    if (frameInclusive && m_gravity == XCB_GRAVITY_STATIC) {
+        r.translate(m_frameMargins.left(), m_frameMargins.top());
+    } else if (!frameInclusive && m_gravity == XCB_GRAVITY_NORTH_WEST) {
+        r.translate(-m_frameMargins.left(), -m_frameMargins.top());
+    }
+    return r;
+}
+
 void QXcbWindow::propagateSizeHints()
 {
     // update WM_NORMAL_HINTS
     xcb_size_hints_t hints;
     memset(&hints, 0, sizeof(hints));
 
-    QRect rect = geometry();
+    const QRect rect = windowToWmGeometry(geometry());
 
     QWindow *win = window();
 
     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, qt_window_private(win)->positionPolicy == QWindowPrivate::WindowFrameInclusive
-                                           ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC);
+    xcb_size_hints_set_win_gravity(&hints, m_gravity);
 
     QSize minimumSize = win->minimumSize();
     QSize maximumSize = win->maximumSize();
@@ -1188,7 +1278,7 @@ void QXcbWindow::requestActivateWindow()
 }
 
 #if XCB_USE_MAEMO_WINDOW_PROPERTIES
-void QXcbWindow::setOrientation(Qt::ScreenOrientation orientation)
+void QXcbWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
 {
     int angle = 0;
     switch (orientation) {
@@ -1266,6 +1356,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
         } else {
             qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
         }
+#ifndef QT_NO_DRAGANDDROP
     } else if (event->type == atom(QXcbAtom::XdndEnter)) {
         connection()->drag()->handleEnter(window(), event);
     } else if (event->type == atom(QXcbAtom::XdndPosition)) {
@@ -1274,6 +1365,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
         connection()->drag()->handleLeave(window(), event);
     } else if (event->type == atom(QXcbAtom::XdndDrop)) {
         connection()->drag()->handleDrop(window(), event);
+#endif
     } else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon
     } else {
         qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
@@ -1589,4 +1681,63 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor)
     xcb_flush(xcb_connection());
 }
 
+bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
+{
+    const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
+    if (!connection()->wmSupport()->isSupportedByWM(moveResize))
+        return false;
+    xcb_client_message_event_t xev;
+    xev.response_type = XCB_CLIENT_MESSAGE;
+    xev.type = moveResize;
+    xev.window = xcb_window();
+    xev.format = 32;
+    const QPoint globalPos = window()->mapToGlobal(pos);
+    xev.data.data32[0] = globalPos.x();
+    xev.data.data32[1] = globalPos.y();
+    const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;
+    const bool left = corner == Qt::BottomLeftCorner || corner == Qt::TopLeftCorner;
+    if (bottom)
+        xev.data.data32[2] = left ? 6 : 4; // bottomleft/bottomright
+    else
+        xev.data.data32[2] = left ? 0 : 2; // topleft/topright
+    xev.data.data32[3] = XCB_BUTTON_INDEX_1;
+    xev.data.data32[4] = 0;
+    xcb_ungrab_pointer(connection()->xcb_connection(), XCB_CURRENT_TIME);
+    xcb_send_event(connection()->xcb_connection(), false, m_screen->root(),
+                   XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
+                   (const char *)&xev);
+    return true;
+}
+
+#if !defined(QT_NO_SHAPE)
+
+static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)
+{
+    xcb_rectangle_t result;
+    result.x = qMax(SHRT_MIN, r.x());
+    result.y = qMax(SHRT_MIN, r.y());
+    result.width = qMin((int)USHRT_MAX, r.width());
+    result.height = qMin((int)USHRT_MAX, r.height());
+    return result;
+}
+
+void QXcbWindow::setMask(const QRegion &region)
+{
+    if (!connection()->hasXShape())
+        return;
+    if (region.isEmpty()) {
+        xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
+                       XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE);
+    } else {
+        QVector<xcb_rectangle_t> rects;
+        foreach (const QRect &r, region.rects())
+            rects.push_back(qRectToXCBRectangle(r));
+        xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
+                             XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
+                             xcb_window(), 0, 0, rects.size(), &rects[0]);
+    }
+}
+
+#endif // !QT_NO_SHAPE
+
 QT_END_NAMESPACE