Windows: Work on QPlatformScreen implementation.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Fri, 20 Jan 2012 10:34:09 +0000 (11:34 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 20 Jan 2012 13:17:41 +0000 (14:17 +0100)
Implement virtual desktops (which is the default for
EnumDisplayMonitors) and change notifications.

Change-Id: Id24a1b6d9766903901ddf1ded8e9933aa03589d4
Reviewed-by: Oliver Wolff <oliver.wolff@nokia.com>
src/plugins/platforms/windows/qtwindowsglobal.h
src/plugins/platforms/windows/qwindowscontext.cpp
src/plugins/platforms/windows/qwindowscontext.h
src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp
src/plugins/platforms/windows/qwindowsintegration.cpp
src/plugins/platforms/windows/qwindowsintegration.h
src/plugins/platforms/windows/qwindowsscreen.cpp
src/plugins/platforms/windows/qwindowsscreen.h

index 599fb0d..02e29fb 100644 (file)
@@ -95,6 +95,7 @@ enum WindowsEventType // Simplify event types
     InputMethodOpenCandidateWindowEvent = InputMethodEventFlag + 4,
     InputMethodCloseCandidateWindowEvent = InputMethodEventFlag + 5,
     InputMethodRequest = InputMethodEventFlag + 6,
+    DisplayChangedEvent = 437,
     UnknownEvent = 542
 };
 
@@ -169,6 +170,8 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
          }
     case WM_GETOBJECT:
         return QtWindows::AccessibleObjectFromWindowRequest;
+    case WM_DISPLAYCHANGE:
+        return QtWindows::DisplayChangedEvent;
     default:
         break;
     }
index 4f42f7f..d0d9279 100644 (file)
@@ -48,6 +48,7 @@
 #include "qwindowsmime.h"
 #include "qwindowsinputcontext.h"
 #include "qwindowsaccessibility.h"
+#include "qwindowsscreen.h"
 
 #include <QtGui/QWindow>
 #include <QtGui/QWindowSystemInterface>
@@ -228,6 +229,7 @@ struct QWindowsContextPrivate {
     QWindowsKeyMapper m_keyMapper;
     QWindowsMouseHandler m_mouseHandler;
     QWindowsMimeConverter m_mimeConverter;
+    QWindowsScreenManager m_screenManager;
     QSharedPointer<QWindowCreationContext> m_creationContext;
     const HRESULT m_oleInitializeResult;
 };
@@ -541,6 +543,11 @@ QWindowsMimeConverter &QWindowsContext::mimeConverter() const
     return d->m_mimeConverter;
 }
 
+QWindowsScreenManager &QWindowsContext::screenManager()
+{
+    return d->m_screenManager;
+}
+
 /*!
     \brief Convenience to create a non-visible, message-only dummy
     window for example used as clipboard watcher or for GL.
@@ -641,6 +648,8 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
         return false;
     case QtWindows::AccessibleObjectFromWindowRequest:
         return QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(hwnd, wParam, lParam, result);
+    case QtWindows::DisplayChangedEvent:
+        return d->m_screenManager.handleDisplayChange(wParam, lParam);
     default:
         break;
     }
index 9a8acbb..c6b3d46 100644 (file)
@@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE
 
 class QWindow;
 class QPlatformScreen;
+class QWindowsScreenManager;
 class QWindowsWindow;
 class QWindowsMimeConverter;
 struct QWindowCreationContext;
@@ -162,6 +163,7 @@ public:
     unsigned systemInfo() const;
 
     QWindowsMimeConverter &mimeConverter() const;
+    QWindowsScreenManager &screenManager();
 
     static QWindowsUser32DLL user32dll;
     static QWindowsShell32DLL shell32dll;
index 4dd409a..f544779 100644 (file)
@@ -202,7 +202,8 @@ messageDebugEntries[] = {
     {WM_IME_COMPOSITION, "WM_IME_COMPOSITION"},
     {WM_IME_ENDCOMPOSITION, "WM_IME_ENDCOMPOSITION"},
     {WM_IME_NOTIFY, "WM_IME_NOTIFY"},
-    {WM_IME_REQUEST, "WM_IME_REQUEST"}
+    {WM_IME_REQUEST, "WM_IME_REQUEST"},
+    {WM_DISPLAYCHANGE, "WM_DISPLAYCHANGE"}
 };
 
 static inline const MessageDebugEntry *messageDebugEntry(UINT msg)
index 8bb8baf..41990e7 100644 (file)
@@ -178,8 +178,7 @@ QWindowsIntegration::QWindowsIntegration() :
 {
     QGuiApplicationPrivate::instance()->setEventDispatcher(d->m_eventDispatcher);
     d->m_clipboard.registerViewer();
-    foreach (QPlatformScreen *pscr, QWindowsScreen::screens())
-        screenAdded(pscr);
+    d->m_context.screenManager().handleScreenChanges();
 }
 
 QWindowsIntegration::~QWindowsIntegration()
index fa133fa..d09605a 100644 (file)
@@ -74,6 +74,8 @@ public:
 
     static QWindowsIntegration *instance();
 
+    inline void emitScreenAdded(QPlatformScreen *s) { screenAdded(s); }
+
 private:
     QScopedPointer<QWindowsIntegrationPrivate> d;
 };
index aadc6ec..d47dc08 100644 (file)
 #include "qwindowsscreen.h"
 #include "qwindowscontext.h"
 #include "qwindowswindow.h"
+#include "qwindowsintegration.h"
 #include "qwindowscursor.h"
+#include "qwindowscontext.h"
 
 #include "qtwindows_additional.h"
 
 #include <QtGui/QPixmap>
 #include <QtGui/QGuiApplication>
+#include <QtGui/QWindowSystemInterface>
 #include <QtGui/QScreen>
 
 #include <QtCore/QDebug>
@@ -55,9 +58,8 @@
 QT_BEGIN_NAMESPACE
 
 QWindowsScreenData::QWindowsScreenData() :
-    dpi(96, 96),
-    depth(32),
-    format(QImage::Format_ARGB32_Premultiplied), primary(false)
+    dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
+    flags(VirtualDesktop), orientation(Qt::LandscapeOrientation)
 {
 }
 
@@ -98,6 +100,8 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM
     data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
     if (HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL)) {
         data.dpi = deviceDPI(hdc);
+        data.depth = GetDeviceCaps(hdc, BITSPIXEL);
+        data.format = data.depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
         data.physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
         DeleteDC(hdc);
     } else {
@@ -107,16 +111,47 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM
     }
     data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
     data.availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
-    data.primary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0;
+    data.orientation = data.geometry.height() > data.geometry.width() ?
+                       Qt::PortraitOrientation : Qt::LandscapeOrientation;
+    // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
+    // virtual desktop screens.
+    data.flags = QWindowsScreenData::VirtualDesktop;
+    if (info.dwFlags & MONITORINFOF_PRIMARY)
+        data.flags |= QWindowsScreenData::PrimaryScreen;
     data.name = QString::fromWCharArray(info.szDevice);
     result->append(data);
     return TRUE;
 }
 
+static inline WindowsScreenDataList monitorData()
+{
+    WindowsScreenDataList result;
+    EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&result);
+    return result;
+}
+
+static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
+{
+    QDebug nospace =  dbg.nospace();
+    nospace << "Screen " << d.name << ' '
+            << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
+            << " avail: "
+            << d.availableGeometry.width() << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' << d.availableGeometry.y()
+            << " physical: " << d.physicalSizeMM.width() <<  'x' << d.physicalSizeMM.height()
+            << " DPI: " << d.dpi.first << 'x' << d.dpi.second << " Depth: " << d.depth
+            << " Format: " << d.format;
+    if (d.flags & QWindowsScreenData::PrimaryScreen)
+        nospace << " primary";
+    if (d.flags & QWindowsScreenData::VirtualDesktop)
+        nospace << " virtual desktop";
+    return dbg;
+}
+
 /*!
     \class QWindowsScreen
     \brief Windows screen.
     \ingroup qt-lighthouse-win
+    \sa QWindowsScreenManager
 */
 
 QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) :
@@ -124,30 +159,6 @@ QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) :
 {
 }
 
-QList<QPlatformScreen *> QWindowsScreen::screens()
-{
-    // Retrieve monitors and add static depth information to each.
-    WindowsScreenDataList data;
-    EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&data);
-
-    const int depth = QWindowsContext::instance()->screenDepth();
-    const QImage::Format format = depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
-    QList<QPlatformScreen *> result;
-
-    const WindowsScreenDataList::const_iterator scend = data.constEnd();
-    for (WindowsScreenDataList::const_iterator it = data.constBegin(); it != scend; ++it) {
-        QWindowsScreenData d = *it;
-        d.depth = depth;
-        d.format = format;
-        if (QWindowsContext::verboseIntegration)
-            qDebug() << "Screen" << d.geometry << d.availableGeometry << d.primary
-                     << " physical " << d.physicalSizeMM << " DPI" << d.dpi
-                     << "Depth: " << d.depth << " Format: " << d.format;
-        result.append(new QWindowsScreen(d));
-    }
-    return result;
-}
-
 QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const
 {
     Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
@@ -227,4 +238,149 @@ QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w)
     return 0;
 }
 
+/*!
+    \brief Determine siblings in a virtual desktop system.
+
+    Self is by definition a sibling, else collect all screens
+    within virtual desktop.
+*/
+
+QList<QPlatformScreen *> QWindowsScreen::virtualSiblings() const
+{
+    QList<QPlatformScreen *> result;
+    if (m_data.flags & QWindowsScreenData::VirtualDesktop) {
+        foreach (QWindowsScreen *screen, QWindowsContext::instance()->screenManager().screens())
+            if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
+                result.push_back(screen);
+    } else {
+        result.push_back(const_cast<QWindowsScreen *>(this));
+    }
+    return result;
+}
+
+/*!
+    \brief Notify QWindowSystemInterface about changes of a screen and synchronize data.
+*/
+
+void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
+{
+    if (m_data.geometry != newData.geometry) {
+        m_data.geometry = newData.geometry;
+        QWindowSystemInterface::handleScreenGeometryChange(screen(),
+                                                           newData.geometry);
+    }
+    if (m_data.availableGeometry != newData.availableGeometry) {
+        m_data.availableGeometry = newData.availableGeometry;
+        QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(),
+                                                                    newData.availableGeometry);
+    }
+    if (!qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
+        || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second)) {
+        m_data.dpi = newData.dpi;
+        QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
+                                                                     newData.dpi.first,
+                                                                     newData.dpi.second);
+    }
+    if (m_data.orientation != newData.orientation) {
+        m_data.orientation = newData.orientation;
+        QWindowSystemInterface::handleScreenOrientationChange(screen(),
+                                                              newData.orientation);
+    }
+}
+
+/*!
+    \class QWindowsScreenManager
+    \brief Manages a list of QWindowsScreen.
+
+    Listens for changes and notifies QWindowSystemInterface about changed/
+    added/deleted screens.
+
+    \ingroup qt-lighthouse-win
+    \sa QWindowsScreen
+*/
+
+QWindowsScreenManager::QWindowsScreenManager() :
+    m_lastDepth(-1), m_lastHorizontalResolution(0), m_lastVerticalResolution(0)
+{
+}
+
+QWindowsScreenManager::~QWindowsScreenManager()
+{
+    qDeleteAll(m_screens);
+}
+
+/*!
+    \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
+
+    Subsequent events are compressed since WM_DISPLAYCHANGE is sent to
+    each top level window.
+*/
+
+bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam)
+{
+    const int newDepth = (int)wParam;
+    const WORD newHorizontalResolution = LOWORD(lParam);
+    const WORD newVerticalResolution = HIWORD(lParam);
+    if (newDepth != m_lastDepth || newHorizontalResolution != m_lastHorizontalResolution
+        || newVerticalResolution != m_lastVerticalResolution) {
+        m_lastDepth = newDepth;
+        m_lastHorizontalResolution = newHorizontalResolution;
+        m_lastVerticalResolution = newVerticalResolution;
+        if (QWindowsContext::verboseWindows)
+            qDebug("%s: Depth=%d, resolution=%hux%hu",
+                   __FUNCTION__, newDepth, newHorizontalResolution, newVerticalResolution);
+        handleScreenChanges();
+    }
+    return false;
+}
+
+static inline int indexOfMonitor(const QList<QWindowsScreen *> &screens,
+                                 const QString &monitorName)
+{
+    for (int i= 0; i < screens.size(); ++i)
+        if (screens.at(i)->data().name == monitorName)
+            return i;
+    return -1;
+}
+
+static inline int indexOfMonitor(const QList<QWindowsScreenData> &screenData,
+                                 const QString &monitorName)
+{
+    for (int i = 0; i < screenData.size(); ++i)
+        if (screenData.at(i).name == monitorName)
+            return i;
+    return -1;
+}
+
+/*!
+    \brief Synchronizes the screen list, adds new screens, removes deleted
+    ones and propagates resolution changes to QWindowSystemInterface.
+*/
+
+void QWindowsScreenManager::handleScreenChanges()
+{
+    // Look for changed monitors, add new ones
+    const WindowsScreenDataList newDataList = monitorData();
+    foreach (const QWindowsScreenData &newData, newDataList) {
+        const int existingIndex = indexOfMonitor(m_screens, newData.name);
+        if (existingIndex != -1) {
+            m_screens.at(existingIndex)->handleChanges(newData);
+        } else {
+            QWindowsScreen *newScreen = new QWindowsScreen(newData);
+            m_screens.push_back(newScreen);
+            QWindowsIntegration::instance()->emitScreenAdded(newScreen);
+            if (QWindowsContext::verboseWindows)
+                qDebug() << "New Monitor: " << newData;
+        }    // exists
+    }        // for new screens.
+    // Remove deleted ones.
+    for (int i = m_screens.size() - 1; i >= 0; --i) {
+        if (indexOfMonitor(newDataList, m_screens.at(i)->data().name) == -1) {
+            if (QWindowsContext::verboseWindows)
+                qDebug() << "Removing Monitor: " << m_screens.at(i) ->data();
+            delete m_screens.takeAt(i);
+        } // not found
+    }     // for existing screens
+}
+
 QT_END_NAMESPACE
index a7b9ba7..bf81087 100644 (file)
@@ -52,6 +52,12 @@ QT_BEGIN_NAMESPACE
 
 struct QWindowsScreenData
 {
+    enum Flags
+    {
+        PrimaryScreen = 0x1,
+        VirtualDesktop = 0x2
+    };
+
     QWindowsScreenData();
 
     QRect geometry;
@@ -60,8 +66,9 @@ struct QWindowsScreenData
     QSizeF physicalSizeMM;
     int depth;
     QImage::Format format;
-    bool primary;
+    unsigned flags;
     QString name;
+    Qt::ScreenOrientation orientation;
 };
 
 class QWindowsScreen : public QPlatformScreen
@@ -78,7 +85,8 @@ public:
     virtual QSizeF physicalSize() const { return m_data.physicalSizeMM; }
     virtual QDpi logicalDpi() const { return m_data.dpi; }
     virtual QString name() const { return m_data.name; }
-
+    virtual Qt::ScreenOrientation primaryOrientation() { return m_data.orientation; }
+    virtual QList<QPlatformScreen *> virtualSiblings() const;
     virtual QWindow *topLevelAt(const QPoint &point) const
         {  return QWindowsScreen::findTopLevelAt(point, CWP_SKIPINVISIBLE);  }
 
@@ -86,18 +94,39 @@ public:
     static QWindow *windowAt(const QPoint &point, unsigned flags = CWP_SKIPINVISIBLE);
     static QWindow *windowUnderMouse(unsigned flags = CWP_SKIPINVISIBLE);
 
-    static QList<QPlatformScreen *> screens();
-
     virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const;
 
-    const QWindowsCursor &cursor() const { return m_cursor; }
-    QWindowsCursor &cursor() { return m_cursor; }
+    inline void handleChanges(const QWindowsScreenData &newData);
+
+    const QWindowsCursor &cursor() const    { return m_cursor; }
+    QWindowsCursor &cursor()                { return m_cursor; }
+
+    const QWindowsScreenData &data() const  { return m_data; }
 
 private:
-    const QWindowsScreenData m_data;
+    QWindowsScreenData m_data;
     QWindowsCursor m_cursor;
 };
 
+class QWindowsScreenManager
+{
+public:
+    typedef QList<QWindowsScreen *> WindowsScreenList;
+
+    QWindowsScreenManager();
+    ~QWindowsScreenManager();
+
+    void handleScreenChanges();
+    bool handleDisplayChange(WPARAM wParam, LPARAM lParam);
+    const WindowsScreenList &screens() const { return m_screens; }
+
+private:
+    WindowsScreenList m_screens;
+    int m_lastDepth;
+    WORD m_lastHorizontalResolution;
+    WORD m_lastVerticalResolution;
+};
+
 QT_END_NAMESPACE
 
 #endif // QWINDOWSSCREEN_H