1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qwindowsscreen.h"
43 #include "qwindowscontext.h"
44 #include "qwindowswindow.h"
45 #include "qwindowsintegration.h"
46 #include "qwindowscursor.h"
48 #include "qtwindows_additional.h"
50 #include <QtGui/QPixmap>
51 #include <QtGui/QGuiApplication>
52 #include <qpa/qwindowsysteminterface.h>
53 #include <QtGui/QScreen>
55 #include <QtCore/QDebug>
59 QWindowsScreenData::QWindowsScreenData() :
60 dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
61 flags(VirtualDesktop), orientation(Qt::LandscapeOrientation),
66 static inline QDpi deviceDPI(HDC hdc)
68 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
71 static inline QSizeF deviceSizeMM(const QSize &pixels, const QDpi &dpi)
73 const qreal inchToMM = 25.4;
74 const qreal h = qreal(pixels.width()) / qreal(dpi.first) * inchToMM;
75 const qreal v = qreal(pixels.height()) / qreal(dpi.second) * inchToMM;
79 static inline QDpi deviceDPI(const QSize &pixels, const QSizeF &physicalSizeMM)
81 const qreal inchToMM = 25.4;
82 const qreal h = qreal(pixels.width()) / (qreal(physicalSizeMM.width()) / inchToMM);
83 const qreal v = qreal(pixels.height()) / (qreal(physicalSizeMM.height()) / inchToMM);
87 typedef QList<QWindowsScreenData> WindowsScreenDataList;
89 // from QDesktopWidget, taking WindowsScreenDataList as LPARAM
90 BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
93 memset(&info, 0, sizeof(MONITORINFOEX));
94 info.cbSize = sizeof(MONITORINFOEX);
95 if (GetMonitorInfo(hMonitor, &info) == FALSE)
98 WindowsScreenDataList *result = reinterpret_cast<WindowsScreenDataList *>(p);
99 QWindowsScreenData data;
100 data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
102 //Windows CE, just supports one Display and expects to get only DISPLAY,
103 //instead of DISPLAY0 and so on, which are passed by info.szDevice
104 HDC hdc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
106 HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL);
109 data.dpi = deviceDPI(hdc);
110 data.depth = GetDeviceCaps(hdc, BITSPIXEL);
111 data.format = data.depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
112 data.physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
113 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
114 if (refreshRate > 1) // 0,1 means heardware default.
115 data.refreshRateHz = refreshRate;
118 qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
119 __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)),
122 data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
123 data.availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
124 data.orientation = data.geometry.height() > data.geometry.width() ?
125 Qt::PortraitOrientation : Qt::LandscapeOrientation;
126 // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
127 // virtual desktop screens.
128 data.flags = QWindowsScreenData::VirtualDesktop;
129 if (info.dwFlags & MONITORINFOF_PRIMARY)
130 data.flags |= QWindowsScreenData::PrimaryScreen;
131 data.name = QString::fromWCharArray(info.szDevice);
132 result->append(data);
136 static inline WindowsScreenDataList monitorData()
138 WindowsScreenDataList result;
139 EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&result);
143 static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
145 QDebug nospace = dbg.nospace();
146 nospace << "Screen " << d.name << ' '
147 << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
149 << d.availableGeometry.width() << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' << d.availableGeometry.y()
150 << " physical: " << d.physicalSizeMM.width() << 'x' << d.physicalSizeMM.height()
151 << " DPI: " << d.dpi.first << 'x' << d.dpi.second << " Depth: " << d.depth
152 << " Format: " << d.format;
153 if (d.flags & QWindowsScreenData::PrimaryScreen)
154 nospace << " primary";
155 if (d.flags & QWindowsScreenData::VirtualDesktop)
156 nospace << " virtual desktop";
160 // Return the cursor to be shared by all screens (virtual desktop).
161 static inline QSharedPointer<QWindowsCursor> sharedCursor()
163 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
164 return static_cast<const QWindowsScreen *>(primaryScreen->handle())->windowsCursor();
165 return QSharedPointer<QWindowsCursor>(new QWindowsCursor);
169 \class QWindowsScreen
170 \brief Windows screen.
171 \sa QWindowsScreenManager
173 \ingroup qt-lighthouse-win
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 = window ? (HWND)window : GetDesktopWindow();
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.
319 \ingroup qt-lighthouse-win
322 QWindowsScreenManager::QWindowsScreenManager() :
323 m_lastDepth(-1), m_lastHorizontalResolution(0), m_lastVerticalResolution(0)
328 \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
330 Subsequent events are compressed since WM_DISPLAYCHANGE is sent to
331 each top level window.
334 bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam)
336 const int newDepth = (int)wParam;
337 const WORD newHorizontalResolution = LOWORD(lParam);
338 const WORD newVerticalResolution = HIWORD(lParam);
339 if (newDepth != m_lastDepth || newHorizontalResolution != m_lastHorizontalResolution
340 || newVerticalResolution != m_lastVerticalResolution) {
341 m_lastDepth = newDepth;
342 m_lastHorizontalResolution = newHorizontalResolution;
343 m_lastVerticalResolution = newVerticalResolution;
344 if (QWindowsContext::verboseWindows)
345 qDebug("%s: Depth=%d, resolution=%hux%hu",
346 __FUNCTION__, newDepth, newHorizontalResolution, newVerticalResolution);
347 handleScreenChanges();
352 static inline int indexOfMonitor(const QList<QWindowsScreen *> &screens,
353 const QString &monitorName)
355 for (int i= 0; i < screens.size(); ++i)
356 if (screens.at(i)->data().name == monitorName)
361 static inline int indexOfMonitor(const QList<QWindowsScreenData> &screenData,
362 const QString &monitorName)
364 for (int i = 0; i < screenData.size(); ++i)
365 if (screenData.at(i).name == monitorName)
371 \brief Synchronizes the screen list, adds new screens, removes deleted
372 ones and propagates resolution changes to QWindowSystemInterface.
375 bool QWindowsScreenManager::handleScreenChanges()
377 // Look for changed monitors, add new ones
378 const WindowsScreenDataList newDataList = monitorData();
379 foreach (const QWindowsScreenData &newData, newDataList) {
380 const int existingIndex = indexOfMonitor(m_screens, newData.name);
381 if (existingIndex != -1) {
382 m_screens.at(existingIndex)->handleChanges(newData);
384 QWindowsScreen *newScreen = new QWindowsScreen(newData);
385 m_screens.push_back(newScreen);
386 QWindowsIntegration::instance()->emitScreenAdded(newScreen);
387 if (QWindowsContext::verboseWindows)
388 qDebug() << "New Monitor: " << newData;
390 } // for new screens.
391 // Remove deleted ones.
392 for (int i = m_screens.size() - 1; i >= 0; --i) {
393 if (indexOfMonitor(newDataList, m_screens.at(i)->data().name) == -1) {
394 if (QWindowsContext::verboseWindows)
395 qDebug() << "Removing Monitor: " << m_screens.at(i) ->data();
396 delete m_screens.takeAt(i);
398 } // for existing screens