Windows: Work on QPlatformScreen implementation.

Implement virtual desktops (which is the default for
EnumDisplayMonitors) and change notifications.

Change-Id: Id24a1b6d9766903901ddf1ded8e9933aa03589d4
Reviewed-by: Oliver Wolff <oliver.wolff@nokia.com>
This commit is contained in:
Friedemann Kleint 2012-01-20 11:34:09 +01:00 committed by Qt by Nokia
parent 688d463f4a
commit fc366046b4
8 changed files with 239 additions and 38 deletions

View 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;
}

View 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;
}

View 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;

View 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)

View 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()

View File

@ -74,6 +74,8 @@ public:
static QWindowsIntegration *instance();
inline void emitScreenAdded(QPlatformScreen *s) { screenAdded(s); }
private:
QScopedPointer<QWindowsIntegrationPrivate> d;
};

View File

@ -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 <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

View 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