QPlatformWindow: Add Window masks.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Mon, 25 Jun 2012 11:58:41 +0000 (13:58 +0200)
committerQt by Nokia <qt-info@nokia.com>
Tue, 26 Jun 2012 12:40:26 +0000 (14:40 +0200)
Add functionality for window masks to QPlatformWindow, which is
required to implement QWidget::setMask() in order to fix
the regression in functionality from Qt 4.8.

Change-Id: I2c2d5629f0b4c6d90e52595ad70b13559aab1f41
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
src/gui/kernel/qplatformintegration.h
src/gui/kernel/qplatformwindow.h
src/gui/kernel/qplatformwindow_qpa.cpp
src/plugins/platforms/windows/qwindowsintegration.cpp
src/plugins/platforms/windows/qwindowswindow.cpp
src/plugins/platforms/windows/qwindowswindow.h
src/plugins/platforms/xcb/qxcbintegration.cpp
src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/qxcbwindow.h
src/plugins/platforms/xcb/xcb.pro
src/widgets/kernel/qwidget_qpa.cpp

index 3990adb..ea13a25 100644 (file)
@@ -85,8 +85,8 @@ public:
         OpenGL = 2,
         ThreadedOpenGL = 3,
         SharedGraphicsCache = 4,
-        BufferQueueingOpenGL = 5
-
+        BufferQueueingOpenGL = 5,
+        WindowMasks = 6
     };
 
     virtual ~QPlatformIntegration() { }
index 6043989..d9f79f2 100644 (file)
@@ -68,6 +68,7 @@ class QPlatformScreen;
 class QPlatformWindowPrivate;
 class QWindow;
 class QIcon;
+class QRegion;
 
 class Q_GUI_EXPORT QPlatformWindow : public QPlatformSurface
 {
@@ -105,6 +106,7 @@ public:
     virtual void propagateSizeHints();
 
     virtual void setOpacity(qreal level);
+    virtual void setMask(const QRegion &region);
     virtual void requestActivateWindow();
 
     virtual void handleContentOrientationChange(Qt::ScreenOrientation orientation);
index 2380c6d..88f2a64 100644 (file)
@@ -242,6 +242,16 @@ void QPlatformWindow::setOpacity(qreal level)
 }
 
 /*!
+  Reimplement to  be able to let Qt set the mask of a window
+*/
+
+void QPlatformWindow::setMask(const QRegion &region)
+{
+    Q_UNUSED(region);
+    qWarning("This plugin does not support setting window masks");
+}
+
+/*!
   Reimplement to let Qt be able to request activation/focus for a window
 
   Some window systems will probably not have callbacks for this functionality,
index 52950ec..7d4308d 100644 (file)
@@ -265,6 +265,8 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co
         return true;
 #  endif // QT_OPENGL_ES_2
 #endif // !QT_NO_OPENGL
+    case WindowMasks:
+        return true;
     default:
         return QPlatformIntegration::hasCapability(cap);
     }
index e49b215..aabea04 100644 (file)
@@ -53,6 +53,7 @@
 #include <QtGui/QGuiApplication>
 #include <QtGui/QScreen>
 #include <QtGui/QWindow>
+#include <QtGui/QRegion>
 #include <private/qwindow_p.h>
 #include <QtGui/QWindowSystemInterface>
 
@@ -1344,6 +1345,49 @@ void QWindowsWindow::setOpacity(qreal level)
     }
 }
 
+static inline HRGN createRectRegion(const QRect &r)
+{
+    return CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+}
+
+static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
+{
+    if (const HRGN rectRegion = createRectRegion(rect)) {
+        HRGN result = CreateRectRgn(0, 0, 0, 0);
+        if (CombineRgn(result, *winRegion, rectRegion, RGN_OR)) {
+            DeleteObject(winRegion);
+            *winRegion = result;
+        }
+        DeleteObject(rectRegion);
+    }
+}
+
+static HRGN qRegionToWinRegion(const QRegion &region)
+{
+    const QVector<QRect> rects = region.rects();
+    if (rects.isEmpty())
+        return NULL;
+    const int rectCount = rects.size();
+    if (rectCount == 1)
+        return createRectRegion(region.boundingRect());
+    HRGN hRegion = createRectRegion(rects.front());
+    for (int i = 1; i < rectCount; ++i)
+        addRectToWinRegion(rects.at(i), &hRegion);
+    return hRegion;
+}
+
+void QWindowsWindow::setMask(const QRegion &region)
+{
+    if (region.isEmpty()) {
+         SetWindowRgn(m_data.hwnd, 0, true);
+         return;
+    }
+    const HRGN winRegion = qRegionToWinRegion(region);
+    // SetWindowRgn takes ownership.
+    if (!SetWindowRgn(m_data.hwnd, winRegion, true))
+        DeleteObject(winRegion);
+}
+
 void QWindowsWindow::requestActivateWindow()
 {
     if (QWindowsContext::verboseWindows)
index 2fd4010..743e10d 100644 (file)
@@ -168,6 +168,7 @@ public:
     virtual QMargins frameMargins() const;
 
     virtual void setOpacity(qreal level);
+    virtual void setMask(const QRegion &region);
     qreal opacity() const { return m_opacity; }
     virtual void requestActivateWindow();
 
index c40ebe4..f7576fe 100644 (file)
@@ -203,6 +203,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
     case ThreadedPixmaps: return true;
     case OpenGL: return true;
     case ThreadedOpenGL: return false;
+    case WindowMasks: return true;
     default: return QPlatformIntegration::hasCapability(cap);
     }
 }
index 59e805f..a4ee2a4 100644 (file)
@@ -44,6 +44,7 @@
 #include <QtDebug>
 #include <QScreen>
 #include <QtGui/QIcon>
+#include <QtGui/QRegion>
 
 #include "qxcbconnection.h"
 #include "qxcbscreen.h"
@@ -97,6 +98,9 @@
 #ifdef XCB_USE_XLIB
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#ifndef QT_NO_SHAPE
+#  include <X11/extensions/shape.h>
+#endif // QT_NO_SHAPE
 #endif
 
 #ifdef XCB_USE_XINPUT2_MAEMO
@@ -1698,4 +1702,54 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
     return true;
 }
 
+#if defined(XCB_USE_XLIB) && !defined(QT_NO_SHAPE)
+
+static inline XRectangle qRectToX11Rectangle(const QRect &r)
+{
+    XRectangle 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;
+}
+
+static inline Region qRegionToX11Region(const QRegion &region)
+{
+    if (region.isEmpty())
+        return None;
+    Region result = XCreateRegion();
+    if (!result)
+        return None;
+    const QVector<QRect> rects = region.rects();
+    if (rects.size() == 1) {
+        XRectangle xrect = qRectToX11Rectangle(region.boundingRect());
+        XUnionRectWithRegion(&xrect, result, result);
+    } else {
+        foreach (const QRect &r, rects) {
+            XRectangle xrect = qRectToX11Rectangle(r);
+            XUnionRectWithRegion(&xrect, result, result);
+        }
+    }
+    return result;
+}
+
+
+void QXcbWindow::setMask(const QRegion &region)
+{
+
+    Display *display = (Display *)connection()->xlib_display();
+    if (region.isEmpty()) {
+        XShapeCombineMask(display, xcb_window(),
+                          ShapeBounding, 0, 0,
+                          None, ShapeSet);
+    } else {
+        XShapeCombineRegion(display, xcb_window(),
+                            ShapeBounding, 0, 0,
+                            qRegionToX11Region(region), ShapeSet);
+    }
+}
+
+#endif // XCB_USE_XLIB && !QT_NO_SHAPE
+
 QT_END_NAMESPACE
index d383248..0c4ec34 100644 (file)
@@ -109,6 +109,10 @@ public:
 
     bool startSystemResize(const QPoint &pos, Qt::Corner corner);
 
+#if defined(XCB_USE_XLIB) && !defined(QT_NO_SHAPE)
+    void setMask(const QRegion &region);
+#endif // XCB_USE_XLIB && !QT_NO_SHAPE
+
     xcb_window_t xcb_window() const { return m_window; }
     uint depth() const { return m_depth; }
     QImage::Format imageFormat() const { return m_imageFormat; }
index 210b770..8e6fbc6 100644 (file)
@@ -45,6 +45,7 @@ contains(QT_CONFIG, xcb-poll-for-queued-event) {
 # needed by GLX, Xcursor, XLookupString, ...
 contains(QT_CONFIG, xcb-xlib) {
     DEFINES += XCB_USE_XLIB
+    !contains(DEFINES, QT_NO_SHAPE):LIBS += -lXext
     LIBS += -lX11 -lX11-xcb
 
     linux-g++-maemo {
index 04bf0be..387ed2e 100644 (file)
@@ -50,6 +50,7 @@
 #include <qpa/qplatformwindow.h>
 #include "QtGui/qsurfaceformat.h"
 #include <qpa/qplatformopenglcontext.h>
+#include <qpa/qplatformintegration.h>
 #include "QtGui/private/qwindow_p.h"
 
 #include <qpa/qplatformcursor.h>
@@ -929,10 +930,24 @@ void QWidgetPrivate::registerDropSite(bool on)
     Q_UNUSED(on);
 }
 
-void QWidgetPrivate::setMask_sys(const QRegion &region)
+void QWidgetPrivate::setMask_sys(const QRegion &regionIn)
 {
-    Q_UNUSED(region);
-    // XXX
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowMasks)) {
+        qWarning("%s: Not supported on %s.", Q_FUNC_INFO, qPrintable(QGuiApplication::platformName()));
+        return;
+    }
+    Q_Q(QWidget);
+    QRegion region = regionIn;
+    QWindow *window = q->windowHandle();
+    if (!window) {
+        if (QWidget *nativeParent = q->nativeParentWidget()) {
+            window = nativeParent->windowHandle();
+            region.translate(q->mapTo(nativeParent, QPoint(0, 0)));
+        }
+    }
+    if (window)
+        if (QPlatformWindow *platformWindow = window->handle())
+            platformWindow->setMask(region);
 }
 
 void QWidgetPrivate::updateFrameStrut()