Windows QScreen: handle change in working area when the taskbar moves
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowsscreen.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
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
48 #include "qtwindows_additional.h"
49
50 #include <QtGui/QPixmap>
51 #include <QtGui/QGuiApplication>
52 #include <qpa/qwindowsysteminterface.h>
53 #include <QtGui/QScreen>
54
55 #include <QtCore/QDebug>
56
57 QT_BEGIN_NAMESPACE
58
59 QWindowsScreenData::QWindowsScreenData() :
60     dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
61     flags(VirtualDesktop), orientation(Qt::LandscapeOrientation),
62     refreshRateHz(60)
63 {
64 }
65
66 static inline QDpi deviceDPI(HDC hdc)
67 {
68     return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
69 }
70
71 static inline QSizeF deviceSizeMM(const QSize &pixels, const QDpi &dpi)
72 {
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;
76     return QSizeF(h, v);
77 }
78
79 static inline QDpi deviceDPI(const QSize &pixels, const QSizeF &physicalSizeMM)
80 {
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);
84     return QDpi(h, v);
85 }
86
87 typedef QList<QWindowsScreenData> WindowsScreenDataList;
88
89 // from QDesktopWidget, taking WindowsScreenDataList as LPARAM
90 BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
91 {
92     MONITORINFOEX info;
93     memset(&info, 0, sizeof(MONITORINFOEX));
94     info.cbSize = sizeof(MONITORINFOEX);
95     if (GetMonitorInfo(hMonitor, &info) == FALSE)
96         return TRUE;
97
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));
101 #ifdef Q_OS_WINCE
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);
105 #else
106     HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL);
107 #endif
108     if (hdc) {
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;
116         DeleteDC(hdc);
117     } else {
118         qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
119                  __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)),
120                  data.dpi.first);
121     }
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);
133     return TRUE;
134 }
135
136 static inline WindowsScreenDataList monitorData()
137 {
138     WindowsScreenDataList result;
139     EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&result);
140     return result;
141 }
142
143 static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
144 {
145     QDebug nospace =  dbg.nospace();
146     nospace << "Screen " << d.name << ' '
147             << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
148             << " avail: "
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";
157     return dbg;
158 }
159
160 // Return the cursor to be shared by all screens (virtual desktop).
161 static inline QSharedPointer<QWindowsCursor> sharedCursor()
162 {
163     if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
164         return static_cast<const QWindowsScreen *>(primaryScreen->handle())->windowsCursor();
165     return QSharedPointer<QWindowsCursor>(new QWindowsCursor);
166 }
167
168 /*!
169     \class QWindowsScreen
170     \brief Windows screen.
171     \sa QWindowsScreenManager
172     \internal
173     \ingroup qt-lighthouse-win
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 = window ? (HWND)window : GetDesktopWindow();
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     \sa QWindowsScreen
318     \internal
319     \ingroup qt-lighthouse-win
320 */
321
322 QWindowsScreenManager::QWindowsScreenManager() :
323     m_lastDepth(-1), m_lastHorizontalResolution(0), m_lastVerticalResolution(0)
324 {
325 }
326
327 /*!
328     \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
329
330     Subsequent events are compressed since WM_DISPLAYCHANGE is sent to
331     each top level window.
332 */
333
334 bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam)
335 {
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();
348     }
349     return false;
350 }
351
352 static inline int indexOfMonitor(const QList<QWindowsScreen *> &screens,
353                                  const QString &monitorName)
354 {
355     for (int i= 0; i < screens.size(); ++i)
356         if (screens.at(i)->data().name == monitorName)
357             return i;
358     return -1;
359 }
360
361 static inline int indexOfMonitor(const QList<QWindowsScreenData> &screenData,
362                                  const QString &monitorName)
363 {
364     for (int i = 0; i < screenData.size(); ++i)
365         if (screenData.at(i).name == monitorName)
366             return i;
367     return -1;
368 }
369
370 /*!
371     \brief Synchronizes the screen list, adds new screens, removes deleted
372     ones and propagates resolution changes to QWindowSystemInterface.
373 */
374
375 bool QWindowsScreenManager::handleScreenChanges()
376 {
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);
383         } else {
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;
389         }    // exists
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);
397         } // not found
398     }     // for existing screens
399     return true;
400 }
401
402 QT_END_NAMESPACE