From fc366046b42e4aa9f10efa81b978c89cfb7206a2 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 20 Jan 2012 11:34:09 +0100 Subject: Windows: Work on QPlatformScreen implementation. Implement virtual desktops (which is the default for EnumDisplayMonitors) and change notifications. Change-Id: Id24a1b6d9766903901ddf1ded8e9933aa03589d4 Reviewed-by: Oliver Wolff --- src/plugins/platforms/windows/qtwindowsglobal.h | 3 + src/plugins/platforms/windows/qwindowscontext.cpp | 9 + src/plugins/platforms/windows/qwindowscontext.h | 2 + .../windows/qwindowsguieventdispatcher.cpp | 3 +- .../platforms/windows/qwindowsintegration.cpp | 3 +- .../platforms/windows/qwindowsintegration.h | 2 + src/plugins/platforms/windows/qwindowsscreen.cpp | 212 ++++++++++++++++++--- src/plugins/platforms/windows/qwindowsscreen.h | 43 ++++- 8 files changed, 239 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 599fb0d201..02e29fbd7f 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -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; } diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 4f42f7fa3b..d0d9279d0a 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -48,6 +48,7 @@ #include "qwindowsmime.h" #include "qwindowsinputcontext.h" #include "qwindowsaccessibility.h" +#include "qwindowsscreen.h" #include #include @@ -228,6 +229,7 @@ struct QWindowsContextPrivate { QWindowsKeyMapper m_keyMapper; QWindowsMouseHandler m_mouseHandler; QWindowsMimeConverter m_mimeConverter; + QWindowsScreenManager m_screenManager; QSharedPointer 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; } diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 9a8acbbb51..c6b3d46e13 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -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; diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp index 4dd409a2ab..f5447798c2 100644 --- a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp +++ b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp @@ -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) diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 8bb8bafe74..41990e7529 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -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() diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index fa133fa5de..d09605a30d 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -74,6 +74,8 @@ public: static QWindowsIntegration *instance(); + inline void emitScreenAdded(QPlatformScreen *s) { screenAdded(s); } + private: QScopedPointer d; }; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index aadc6ec986..d47dc08f39 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -42,12 +42,15 @@ #include "qwindowsscreen.h" #include "qwindowscontext.h" #include "qwindowswindow.h" +#include "qwindowsintegration.h" #include "qwindowscursor.h" +#include "qwindowscontext.h" #include "qtwindows_additional.h" #include #include +#include #include #include @@ -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 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 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 QWindowsScreen::virtualSiblings() const +{ + QList 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(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 &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 &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 diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index a7b9ba7fcc..bf81087e39 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -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 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 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 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 -- cgit v1.2.3