0c24ff7aa6e533ea66e96a63536dd345abaabec0
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowsscreen.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qwindowsscreen.h"
43 #include "qwindowscontext.h"
44 #include "qwindowswindow.h"
45 #include "qwindowsintegration.h"
46 #include "qwindowscursor.h"
47 #include "qwindowscontext.h"
48
49 #include "qtwindows_additional.h"
50
51 #include <QtGui/QPixmap>
52 #include <QtGui/QGuiApplication>
53 #include <QtGui/QWindowSystemInterface>
54 #include <QtGui/QScreen>
55
56 #include <QtCore/QDebug>
57
58 QT_BEGIN_NAMESPACE
59
60 QWindowsScreenData::QWindowsScreenData() :
61     dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
62     flags(VirtualDesktop), orientation(Qt::LandscapeOrientation),
63     refreshRateHz(60)
64 {
65 }
66
67 static inline QDpi deviceDPI(HDC hdc)
68 {
69     return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
70 }
71
72 static inline QSizeF deviceSizeMM(const QSize &pixels, const QDpi &dpi)
73 {
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;
77     return QSizeF(h, v);
78 }
79
80 static inline QDpi deviceDPI(const QSize &pixels, const QSizeF &physicalSizeMM)
81 {
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);
85     return QDpi(h, v);
86 }
87
88 typedef QList<QWindowsScreenData> WindowsScreenDataList;
89
90 // from QDesktopWidget, taking WindowsScreenDataList as LPARAM
91 BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
92 {
93     MONITORINFOEX info;
94     memset(&info, 0, sizeof(MONITORINFOEX));
95     info.cbSize = sizeof(MONITORINFOEX);
96     if (GetMonitorInfo(hMonitor, &info) == FALSE)
97         return TRUE;
98
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));
102 #ifdef Q_OS_WINCE
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);
106 #else
107     HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL);
108 #endif
109     if (hdc) {
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;
117         DeleteDC(hdc);
118     } else {
119         qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
120                  __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)),
121                  data.dpi.first);
122     }
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);
134     return TRUE;
135 }
136
137 static inline WindowsScreenDataList monitorData()
138 {
139     WindowsScreenDataList result;
140     EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&result);
141     return result;
142 }
143
144 static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
145 {
146     QDebug nospace =  dbg.nospace();
147     nospace << "Screen " << d.name << ' '
148             << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
149             << " avail: "
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";
158     return dbg;
159 }
160
161 // Return the cursor to be shared by all screens (virtual desktop).
162 static inline QSharedPointer<QWindowsCursor> sharedCursor()
163 {
164     if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
165         return static_cast<const QWindowsScreen *>(primaryScreen->handle())->windowsCursor();
166     return QSharedPointer<QWindowsCursor>(new QWindowsCursor);
167 }
168
169 /*!
170     \class QWindowsScreen
171     \brief Windows screen.
172     \ingroup qt-lighthouse-win
173     \sa QWindowsScreenManager
174 */
175
176 QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) :
177     m_data(data), m_cursor(sharedCursor())
178 {
179 }
180
181 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
182
183 QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const
184 {
185     if (QWindowsContext::verboseIntegration)
186         qDebug() << __FUNCTION__ << window << x << y << width << height;
187     RECT r;
188     HWND hwnd = (HWND)window;
189     GetClientRect(hwnd, &r);
190
191     if (width < 0) width = r.right - r.left;
192     if (height < 0) height = r.bottom - r.top;
193
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);
199
200     // copy data
201     HDC window_dc = GetDC(hwnd);
202     BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
203
204     // clean up all but bitmap
205     ReleaseDC(hwnd, window_dc);
206     SelectObject(bitmap_dc, null_bitmap);
207     DeleteDC(bitmap_dc);
208
209     const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
210
211     DeleteObject(bitmap);
212     ReleaseDC(0, display_dc);
213
214     return pixmap;
215 }
216
217 /*!
218     \brief Find a top level window taking the flags of ChildWindowFromPointEx.
219 */
220
221 QWindow *QWindowsScreen::findTopLevelAt(const QPoint &point, unsigned flags)
222 {
223     QWindow* result = 0;
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;
229     return result;
230 }
231
232 QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
233 {
234     QWindow* result = 0;
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;
240     return result;
241 }
242
243 QWindow *QWindowsScreen::windowUnderMouse(unsigned flags)
244 {
245     return QWindowsScreen::windowAt(QWindowsCursor::mousePosition(), flags);
246 }
247
248 QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w)
249 {
250     if (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);
257     return 0;
258 }
259
260 /*!
261     \brief Determine siblings in a virtual desktop system.
262
263     Self is by definition a sibling, else collect all screens
264     within virtual desktop.
265 */
266
267 QList<QPlatformScreen *> QWindowsScreen::virtualSiblings() const
268 {
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);
274     } else {
275         result.push_back(const_cast<QWindowsScreen *>(this));
276     }
277     return result;
278 }
279
280 /*!
281     \brief Notify QWindowSystemInterface about changes of a screen and synchronize data.
282 */
283
284 void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
285 {
286     if (m_data.geometry != newData.geometry) {
287         m_data.geometry = newData.geometry;
288         QWindowSystemInterface::handleScreenGeometryChange(screen(),
289                                                            newData.geometry);
290     }
291     if (m_data.availableGeometry != newData.availableGeometry) {
292         m_data.availableGeometry = newData.availableGeometry;
293         QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(),
294                                                                     newData.availableGeometry);
295     }
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(),
300                                                                      newData.dpi.first,
301                                                                      newData.dpi.second);
302     }
303     if (m_data.orientation != newData.orientation) {
304         m_data.orientation = newData.orientation;
305         QWindowSystemInterface::handleScreenOrientationChange(screen(),
306                                                               newData.orientation);
307     }
308 }
309
310 /*!
311     \class QWindowsScreenManager
312     \brief Manages a list of QWindowsScreen.
313
314     Listens for changes and notifies QWindowSystemInterface about changed/
315     added/deleted screens.
316
317     \ingroup qt-lighthouse-win
318     \sa QWindowsScreen
319 */
320
321 QWindowsScreenManager::QWindowsScreenManager() :
322     m_lastDepth(-1), m_lastHorizontalResolution(0), m_lastVerticalResolution(0)
323 {
324 }
325
326 /*!
327     \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
328
329     Subsequent events are compressed since WM_DISPLAYCHANGE is sent to
330     each top level window.
331 */
332
333 bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam)
334 {
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();
347     }
348     return false;
349 }
350
351 static inline int indexOfMonitor(const QList<QWindowsScreen *> &screens,
352                                  const QString &monitorName)
353 {
354     for (int i= 0; i < screens.size(); ++i)
355         if (screens.at(i)->data().name == monitorName)
356             return i;
357     return -1;
358 }
359
360 static inline int indexOfMonitor(const QList<QWindowsScreenData> &screenData,
361                                  const QString &monitorName)
362 {
363     for (int i = 0; i < screenData.size(); ++i)
364         if (screenData.at(i).name == monitorName)
365             return i;
366     return -1;
367 }
368
369 /*!
370     \brief Synchronizes the screen list, adds new screens, removes deleted
371     ones and propagates resolution changes to QWindowSystemInterface.
372 */
373
374 void QWindowsScreenManager::handleScreenChanges()
375 {
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);
382         } else {
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;
388         }    // exists
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);
396         } // not found
397     }     // for existing screens
398 }
399
400 QT_END_NAMESPACE