1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qwindowsscreen.h"
43 #include "qwindowscontext.h"
44 #include "qwindowswindow.h"
45 #include "qwindowsintegration.h"
46 #include "qwindowscursor.h"
47 #include "qwindowscontext.h"
49 #include "qtwindows_additional.h"
51 #include <QtGui/QPixmap>
52 #include <QtGui/QGuiApplication>
53 #include <QtGui/QWindowSystemInterface>
54 #include <QtGui/QScreen>
56 #include <QtCore/QDebug>
60 QWindowsScreenData::QWindowsScreenData() :
61 dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
62 flags(VirtualDesktop), orientation(Qt::LandscapeOrientation),
67 static inline QDpi deviceDPI(HDC hdc)
69 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
72 static inline QSizeF deviceSizeMM(const QSize &pixels, const QDpi &dpi)
74 const qreal inchToMM = 25.4;
75 const qreal h = qreal(pixels.width()) / qreal(dpi.first) * inchToMM;
76 const qreal v = qreal(pixels.height()) / qreal(dpi.second) * inchToMM;
80 static inline QDpi deviceDPI(const QSize &pixels, const QSizeF &physicalSizeMM)
82 const qreal inchToMM = 25.4;
83 const qreal h = qreal(pixels.width()) / (qreal(physicalSizeMM.width()) / inchToMM);
84 const qreal v = qreal(pixels.height()) / (qreal(physicalSizeMM.height()) / inchToMM);
88 typedef QList<QWindowsScreenData> WindowsScreenDataList;
90 // from QDesktopWidget, taking WindowsScreenDataList as LPARAM
91 BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
94 memset(&info, 0, sizeof(MONITORINFOEX));
95 info.cbSize = sizeof(MONITORINFOEX);
96 if (GetMonitorInfo(hMonitor, &info) == FALSE)
99 WindowsScreenDataList *result = reinterpret_cast<WindowsScreenDataList *>(p);
100 QWindowsScreenData data;
101 data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
103 //Windows CE, just supports one Display and expects to get only DISPLAY,
104 //instead of DISPLAY0 and so on, which are passed by info.szDevice
105 HDC hdc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
107 HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL);
110 data.dpi = deviceDPI(hdc);
111 data.depth = GetDeviceCaps(hdc, BITSPIXEL);
112 data.format = data.depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
113 data.physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
114 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
115 if (refreshRate > 1) // 0,1 means heardware default.
116 data.refreshRateHz = refreshRate;
119 qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
120 __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)),
123 data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
124 data.availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
125 data.orientation = data.geometry.height() > data.geometry.width() ?
126 Qt::PortraitOrientation : Qt::LandscapeOrientation;
127 // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
128 // virtual desktop screens.
129 data.flags = QWindowsScreenData::VirtualDesktop;
130 if (info.dwFlags & MONITORINFOF_PRIMARY)
131 data.flags |= QWindowsScreenData::PrimaryScreen;
132 data.name = QString::fromWCharArray(info.szDevice);
133 result->append(data);
137 static inline WindowsScreenDataList monitorData()
139 WindowsScreenDataList result;
140 EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&result);
144 static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
146 QDebug nospace = dbg.nospace();
147 nospace << "Screen " << d.name << ' '
148 << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
150 << d.availableGeometry.width() << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' << d.availableGeometry.y()
151 << " physical: " << d.physicalSizeMM.width() << 'x' << d.physicalSizeMM.height()
152 << " DPI: " << d.dpi.first << 'x' << d.dpi.second << " Depth: " << d.depth
153 << " Format: " << d.format;
154 if (d.flags & QWindowsScreenData::PrimaryScreen)
155 nospace << " primary";
156 if (d.flags & QWindowsScreenData::VirtualDesktop)
157 nospace << " virtual desktop";
161 // Return the cursor to be shared by all screens (virtual desktop).
162 static inline QSharedPointer<QWindowsCursor> sharedCursor()
164 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
165 return static_cast<const QWindowsScreen *>(primaryScreen->handle())->windowsCursor();
166 return QSharedPointer<QWindowsCursor>(new QWindowsCursor);
170 \class QWindowsScreen
171 \brief Windows screen.
172 \ingroup qt-lighthouse-win
173 \sa QWindowsScreenManager
176 QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) :
177 m_data(data), m_cursor(sharedCursor())
181 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
183 QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const
185 if (QWindowsContext::verboseIntegration)
186 qDebug() << __FUNCTION__ << window << x << y << width << height;
188 HWND hwnd = (HWND)window;
189 GetClientRect(hwnd, &r);
191 if (width < 0) width = r.right - r.left;
192 if (height < 0) height = r.bottom - r.top;
194 // Create and setup bitmap
195 HDC display_dc = GetDC(0);
196 HDC bitmap_dc = CreateCompatibleDC(display_dc);
197 HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
198 HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
201 HDC window_dc = GetDC(hwnd);
202 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
204 // clean up all but bitmap
205 ReleaseDC(hwnd, window_dc);
206 SelectObject(bitmap_dc, null_bitmap);
209 const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
211 DeleteObject(bitmap);
212 ReleaseDC(0, display_dc);
218 \brief Find a top level window taking the flags of ChildWindowFromPointEx.
221 QWindow *QWindowsScreen::findTopLevelAt(const QPoint &point, unsigned flags)
224 if (QPlatformWindow *bw = QWindowsContext::instance()->
225 findPlatformWindowAt(GetDesktopWindow(), point, flags))
226 result = QWindowsWindow::topLevelOf(bw->window());
227 if (QWindowsContext::verboseWindows)
228 qDebug() << __FUNCTION__ << point << flags << result;
232 QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
235 if (QPlatformWindow *bw = QWindowsContext::instance()->
236 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
237 result = bw->window();
238 if (QWindowsContext::verboseWindows)
239 qDebug() << __FUNCTION__ << screenPoint << " returns " << result;
243 QWindow *QWindowsScreen::windowUnderMouse(unsigned flags)
245 return QWindowsScreen::windowAt(QWindowsCursor::mousePosition(), flags);
248 QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w)
251 if (const QScreen *s = w->screen())
252 if (QPlatformScreen *pscr = s->handle())
253 return static_cast<QWindowsScreen *>(pscr);
254 if (const QScreen *ps = QGuiApplication::primaryScreen())
255 if (QPlatformScreen *ppscr = ps->handle())
256 return static_cast<QWindowsScreen *>(ppscr);
261 \brief Determine siblings in a virtual desktop system.
263 Self is by definition a sibling, else collect all screens
264 within virtual desktop.
267 QList<QPlatformScreen *> QWindowsScreen::virtualSiblings() const
269 QList<QPlatformScreen *> result;
270 if (m_data.flags & QWindowsScreenData::VirtualDesktop) {
271 foreach (QWindowsScreen *screen, QWindowsContext::instance()->screenManager().screens())
272 if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
273 result.push_back(screen);
275 result.push_back(const_cast<QWindowsScreen *>(this));
281 \brief Notify QWindowSystemInterface about changes of a screen and synchronize data.
284 void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
286 if (m_data.geometry != newData.geometry) {
287 m_data.geometry = newData.geometry;
288 QWindowSystemInterface::handleScreenGeometryChange(screen(),
291 if (m_data.availableGeometry != newData.availableGeometry) {
292 m_data.availableGeometry = newData.availableGeometry;
293 QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(),
294 newData.availableGeometry);
296 if (!qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
297 || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second)) {
298 m_data.dpi = newData.dpi;
299 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
303 if (m_data.orientation != newData.orientation) {
304 m_data.orientation = newData.orientation;
305 QWindowSystemInterface::handleScreenOrientationChange(screen(),
306 newData.orientation);
311 \class QWindowsScreenManager
312 \brief Manages a list of QWindowsScreen.
314 Listens for changes and notifies QWindowSystemInterface about changed/
315 added/deleted screens.
317 \ingroup qt-lighthouse-win
321 QWindowsScreenManager::QWindowsScreenManager() :
322 m_lastDepth(-1), m_lastHorizontalResolution(0), m_lastVerticalResolution(0)
327 \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
329 Subsequent events are compressed since WM_DISPLAYCHANGE is sent to
330 each top level window.
333 bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam)
335 const int newDepth = (int)wParam;
336 const WORD newHorizontalResolution = LOWORD(lParam);
337 const WORD newVerticalResolution = HIWORD(lParam);
338 if (newDepth != m_lastDepth || newHorizontalResolution != m_lastHorizontalResolution
339 || newVerticalResolution != m_lastVerticalResolution) {
340 m_lastDepth = newDepth;
341 m_lastHorizontalResolution = newHorizontalResolution;
342 m_lastVerticalResolution = newVerticalResolution;
343 if (QWindowsContext::verboseWindows)
344 qDebug("%s: Depth=%d, resolution=%hux%hu",
345 __FUNCTION__, newDepth, newHorizontalResolution, newVerticalResolution);
346 handleScreenChanges();
351 static inline int indexOfMonitor(const QList<QWindowsScreen *> &screens,
352 const QString &monitorName)
354 for (int i= 0; i < screens.size(); ++i)
355 if (screens.at(i)->data().name == monitorName)
360 static inline int indexOfMonitor(const QList<QWindowsScreenData> &screenData,
361 const QString &monitorName)
363 for (int i = 0; i < screenData.size(); ++i)
364 if (screenData.at(i).name == monitorName)
370 \brief Synchronizes the screen list, adds new screens, removes deleted
371 ones and propagates resolution changes to QWindowSystemInterface.
374 void QWindowsScreenManager::handleScreenChanges()
376 // Look for changed monitors, add new ones
377 const WindowsScreenDataList newDataList = monitorData();
378 foreach (const QWindowsScreenData &newData, newDataList) {
379 const int existingIndex = indexOfMonitor(m_screens, newData.name);
380 if (existingIndex != -1) {
381 m_screens.at(existingIndex)->handleChanges(newData);
383 QWindowsScreen *newScreen = new QWindowsScreen(newData);
384 m_screens.push_back(newScreen);
385 QWindowsIntegration::instance()->emitScreenAdded(newScreen);
386 if (QWindowsContext::verboseWindows)
387 qDebug() << "New Monitor: " << newData;
389 } // for new screens.
390 // Remove deleted ones.
391 for (int i = m_screens.size() - 1; i >= 0; --i) {
392 if (indexOfMonitor(newDataList, m_screens.at(i)->data().name) == -1) {
393 if (QWindowsContext::verboseWindows)
394 qDebug() << "Removing Monitor: " << m_screens.at(i) ->data();
395 delete m_screens.takeAt(i);
397 } // for existing screens