| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwindowsscreen.h" |
| #include "qwindowscontext.h" |
| #include "qwindowswindow.h" |
| #include "qwindowsintegration.h" |
| #include "qwindowscursor.h" |
| #include "qwindowstheme.h" |
| |
| #include <QtCore/qt_windows.h> |
| |
| #include <QtCore/qsettings.h> |
| #include <QtGui/qpixmap.h> |
| #include <QtGui/qguiapplication.h> |
| #include <qpa/qwindowsysteminterface.h> |
| #include <private/qhighdpiscaling_p.h> |
| #include <QtGui/qscreen.h> |
| |
| #include <QtCore/qdebug.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static inline QDpi deviceDPI(HDC hdc) |
| { |
| return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); |
| } |
| |
| static inline QDpi monitorDPI(HMONITOR hMonitor) |
| { |
| if (QWindowsContext::shcoredll.isValid()) { |
| UINT dpiX; |
| UINT dpiY; |
| if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) |
| return QDpi(dpiX, dpiY); |
| } |
| return {0, 0}; |
| } |
| |
| using WindowsScreenDataList = QList<QWindowsScreenData>; |
| |
| static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) |
| { |
| MONITORINFOEX info; |
| memset(&info, 0, sizeof(MONITORINFOEX)); |
| info.cbSize = sizeof(MONITORINFOEX); |
| if (GetMonitorInfo(hMonitor, &info) == FALSE) |
| return false; |
| |
| data->hMonitor = hMonitor; |
| 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->name = QString::fromWCharArray(info.szDevice); |
| if (data->name == QLatin1String("WinDisc")) { |
| data->flags |= QWindowsScreenData::LockScreen; |
| } else { |
| if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) { |
| const QDpi dpi = monitorDPI(hMonitor); |
| data->dpi = dpi.first > 0 ? 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)); |
| const int refreshRate = GetDeviceCaps(hdc, VREFRESH); |
| if (refreshRate > 1) // 0,1 means hardware default. |
| data->refreshRateHz = refreshRate; |
| DeleteDC(hdc); |
| } else { |
| qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.", |
| __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)), |
| data->dpi.first); |
| } // CreateDC() failed |
| } // not lock screen |
| 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; |
| return true; |
| } |
| |
| // from QDesktopWidget, taking WindowsScreenDataList as LPARAM |
| BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) |
| { |
| QWindowsScreenData data; |
| if (monitorData(hMonitor, &data)) { |
| auto *result = reinterpret_cast<WindowsScreenDataList *>(p); |
| // QWindowSystemInterface::handleScreenAdded() documentation specifies that first |
| // added screen will be the primary screen, so order accordingly. |
| // Note that the side effect of this policy is that there is no way to change primary |
| // screen reported by Qt, unless we want to delete all existing screens and add them |
| // again whenever primary screen changes. |
| if (data.flags & QWindowsScreenData::PrimaryScreen) |
| result->prepend(data); |
| else |
| result->append(data); |
| } |
| return TRUE; |
| } |
| |
| static inline WindowsScreenDataList monitorData() |
| { |
| WindowsScreenDataList result; |
| EnumDisplayMonitors(nullptr, nullptr, monitorEnumCallback, reinterpret_cast<LPARAM>(&result)); |
| return result; |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) |
| { |
| QDebugStateSaver saver(dbg); |
| dbg.nospace(); |
| dbg.noquote(); |
| dbg << "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 |
| << " hMonitor: " << d.hMonitor; |
| if (d.flags & QWindowsScreenData::PrimaryScreen) |
| dbg << " primary"; |
| if (d.flags & QWindowsScreenData::VirtualDesktop) |
| dbg << " virtual desktop"; |
| if (d.flags & QWindowsScreenData::LockScreen) |
| dbg << " lock screen"; |
| return dbg; |
| } |
| #endif // !QT_NO_DEBUG_STREAM |
| |
| /*! |
| \class QWindowsScreen |
| \brief Windows screen. |
| \sa QWindowsScreenManager |
| \internal |
| \ingroup qt-lighthouse-win |
| */ |
| |
| QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : |
| m_data(data) |
| #ifndef QT_NO_CURSOR |
| , m_cursor(new QWindowsCursor(this)) |
| #endif |
| { |
| } |
| |
| Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0); |
| |
| QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const |
| { |
| QSize windowSize; |
| int x = xIn; |
| int y = yIn; |
| HWND hwnd = reinterpret_cast<HWND>(window); |
| if (hwnd) { |
| RECT r; |
| GetClientRect(hwnd, &r); |
| windowSize = QSize(r.right - r.left, r.bottom - r.top); |
| } else { |
| // Grab current screen. The client rectangle of GetDesktopWindow() is the |
| // primary screen, but it is possible to grab other screens from it. |
| hwnd = GetDesktopWindow(); |
| const QRect screenGeometry = geometry(); |
| windowSize = screenGeometry.size(); |
| x += screenGeometry.x(); |
| y += screenGeometry.y(); |
| } |
| |
| if (width < 0) |
| width = windowSize.width() - xIn; |
| if (height < 0) |
| height = windowSize.height() - yIn; |
| |
| // Create and setup bitmap |
| HDC display_dc = GetDC(nullptr); |
| HDC bitmap_dc = CreateCompatibleDC(display_dc); |
| HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height); |
| HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap); |
| |
| // copy data |
| HDC window_dc = GetDC(hwnd); |
| BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT); |
| |
| // clean up all but bitmap |
| ReleaseDC(hwnd, window_dc); |
| SelectObject(bitmap_dc, null_bitmap); |
| DeleteDC(bitmap_dc); |
| |
| const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap); |
| |
| DeleteObject(bitmap); |
| ReleaseDC(nullptr, display_dc); |
| |
| return pixmap; |
| } |
| |
| /*! |
| \brief Find a top level window taking the flags of ChildWindowFromPointEx. |
| */ |
| |
| QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const |
| { |
| QWindow *result = nullptr; |
| if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) |
| result = QWindowsWindow::topLevelOf(child); |
| if (QWindowsContext::verbose > 1) |
| qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; |
| return result; |
| } |
| |
| QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) |
| { |
| QWindow* result = nullptr; |
| if (QPlatformWindow *bw = QWindowsContext::instance()-> |
| findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) |
| result = bw->window(); |
| if (QWindowsContext::verbose > 1) |
| qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; |
| return result; |
| } |
| |
| /*! |
| \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) { |
| const QWindowsScreenManager::WindowsScreenList screens |
| = QWindowsContext::instance()->screenManager().screens(); |
| for (QWindowsScreen *screen : 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) |
| { |
| m_data.physicalSizeMM = newData.physicalSizeMM; |
| |
| if (m_data.hMonitor != newData.hMonitor) { |
| qCDebug(lcQpaWindows) << "Monitor" << m_data.name |
| << "has had its hMonitor handle changed from" |
| << m_data.hMonitor << "to" << newData.hMonitor; |
| m_data.hMonitor = newData.hMonitor; |
| } |
| |
| // QGuiApplicationPrivate::processScreenGeometryChange() checks and emits |
| // DPI and orientation as well, so, assign new values and emit DPI first. |
| const bool geometryChanged = m_data.geometry != newData.geometry |
| || m_data.availableGeometry != newData.availableGeometry; |
| const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first) |
| || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second); |
| const bool orientationChanged = m_data.orientation != newData.orientation; |
| m_data.dpi = newData.dpi; |
| m_data.orientation = newData.orientation; |
| m_data.geometry = newData.geometry; |
| m_data.availableGeometry = newData.availableGeometry; |
| |
| if (dpiChanged) { |
| QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), |
| newData.dpi.first, |
| newData.dpi.second); |
| } |
| if (orientationChanged) |
| QWindowSystemInterface::handleScreenOrientationChange(screen(), newData.orientation); |
| if (geometryChanged) { |
| QWindowSystemInterface::handleScreenGeometryChange(screen(), |
| newData.geometry, newData.availableGeometry); |
| } |
| } |
| |
| HMONITOR QWindowsScreen::handle() const |
| { |
| return m_data.hMonitor; |
| } |
| |
| QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry() |
| { |
| QRect result; |
| const auto siblings = screen->virtualSiblings(); |
| for (const QPlatformScreen *sibling : siblings) |
| result |= sibling->geometry(); |
| return result; |
| } |
| |
| enum OrientationPreference : DWORD // matching Win32 API ORIENTATION_PREFERENCE |
| { |
| orientationPreferenceNone = 0, |
| orientationPreferenceLandscape = 0x1, |
| orientationPreferencePortrait = 0x2, |
| orientationPreferenceLandscapeFlipped = 0x4, |
| orientationPreferencePortraitFlipped = 0x8 |
| }; |
| |
| bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o) |
| { |
| bool result = false; |
| if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) { |
| DWORD orientationPreference = 0; |
| switch (o) { |
| case Qt::PrimaryOrientation: |
| orientationPreference = orientationPreferenceNone; |
| break; |
| case Qt::PortraitOrientation: |
| orientationPreference = orientationPreferencePortrait; |
| break; |
| case Qt::LandscapeOrientation: |
| orientationPreference = orientationPreferenceLandscape; |
| break; |
| case Qt::InvertedPortraitOrientation: |
| orientationPreference = orientationPreferencePortraitFlipped; |
| break; |
| case Qt::InvertedLandscapeOrientation: |
| orientationPreference = orientationPreferenceLandscapeFlipped; |
| break; |
| } |
| result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference); |
| } |
| return result; |
| } |
| |
| Qt::ScreenOrientation QWindowsScreen::orientationPreference() |
| { |
| Qt::ScreenOrientation result = Qt::PrimaryOrientation; |
| if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) { |
| DWORD orientationPreference = 0; |
| if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) { |
| switch (orientationPreference) { |
| case orientationPreferenceLandscape: |
| result = Qt::LandscapeOrientation; |
| break; |
| case orientationPreferencePortrait: |
| result = Qt::PortraitOrientation; |
| break; |
| case orientationPreferenceLandscapeFlipped: |
| result = Qt::InvertedLandscapeOrientation; |
| break; |
| case orientationPreferencePortraitFlipped: |
| result = Qt::InvertedPortraitOrientation; |
| break; |
| } |
| } |
| } |
| return result; |
| } |
| |
| /*! |
| \brief Queries ClearType settings to check the pixel layout |
| */ |
| QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTypeHint() const |
| { |
| QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); |
| if (type == QPlatformScreen::Subpixel_None) { |
| QSettings settings(QLatin1String(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"), |
| QSettings::NativeFormat); |
| int registryValue = settings.value(QLatin1String("PixelStructure"), -1).toInt(); |
| switch (registryValue) { |
| case 0: |
| type = QPlatformScreen::Subpixel_None; |
| break; |
| case 1: |
| type = QPlatformScreen::Subpixel_RGB; |
| break; |
| case 2: |
| type = QPlatformScreen::Subpixel_BGR; |
| break; |
| default: |
| type = QPlatformScreen::Subpixel_None; |
| break; |
| } |
| } |
| return type; |
| } |
| |
| /*! |
| \class QWindowsScreenManager |
| \brief Manages a list of QWindowsScreen. |
| |
| Listens for changes and notifies QWindowSystemInterface about changed/ |
| added/deleted screens. |
| |
| \sa QWindowsScreen |
| \internal |
| \ingroup qt-lighthouse-win |
| */ |
| |
| QWindowsScreenManager::QWindowsScreenManager() = default; |
| |
| |
| bool QWindowsScreenManager::isSingleScreen() |
| { |
| return QWindowsContext::instance()->screenManager().screens().size() < 2; |
| } |
| |
| /*! |
| \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; |
| qCDebug(lcQpaWindows) << __FUNCTION__ << "Depth=" << newDepth |
| << ", resolution " << newHorizontalResolution << 'x' << 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; |
| } |
| |
| // Move a window to a new virtual screen, accounting for varying sizes. |
| static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen) |
| { |
| QRect geometry = w->geometry(); |
| const QRect oldScreenGeometry = w->screen()->geometry(); |
| const QRect newScreenGeometry = newScreen->geometry(); |
| QPoint relativePosition = geometry.topLeft() - oldScreenGeometry.topLeft(); |
| if (oldScreenGeometry.size() != newScreenGeometry.size()) { |
| const qreal factor = |
| qreal(QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) / |
| qreal(QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength()); |
| relativePosition = (QPointF(relativePosition) * factor).toPoint(); |
| } |
| geometry.moveTopLeft(relativePosition); |
| w->setGeometry(geometry); |
| } |
| |
| void QWindowsScreenManager::removeScreen(int index) |
| { |
| qCDebug(lcQpaWindows) << "Removing Monitor:" << m_screens.at(index)->data(); |
| QScreen *screen = m_screens.at(index)->screen(); |
| QScreen *primaryScreen = QGuiApplication::primaryScreen(); |
| // QTBUG-38650: When a screen is disconnected, Windows will automatically |
| // move the Window to another screen. This will trigger a geometry change |
| // event, but unfortunately after the screen destruction signal. To prevent |
| // QtGui from automatically hiding the QWindow, pretend all Windows move to |
| // the primary screen first (which is likely the correct, final screen). |
| // QTBUG-39320: Windows does not automatically move WS_EX_TOOLWINDOW (dock) windows; |
| // move those manually. |
| if (screen != primaryScreen) { |
| unsigned movedWindowCount = 0; |
| const QWindowList tlws = QGuiApplication::topLevelWindows(); |
| for (QWindow *w : tlws) { |
| if (w->screen() == screen && w->handle() && w->type() != Qt::Desktop) { |
| if (w->isVisible() && w->windowState() != Qt::WindowMinimized |
| && (QWindowsWindow::baseWindowOf(w)->exStyle() & WS_EX_TOOLWINDOW)) { |
| moveToVirtualScreen(w, primaryScreen); |
| } else { |
| QWindowSystemInterface::handleWindowScreenChanged(w, primaryScreen); |
| } |
| ++movedWindowCount; |
| } |
| } |
| if (movedWindowCount) |
| QWindowSystemInterface::flushWindowSystemEvents(); |
| } |
| QWindowSystemInterface::handleScreenRemoved(m_screens.takeAt(index)); |
| } |
| |
| /*! |
| \brief Synchronizes the screen list, adds new screens, removes deleted |
| ones and propagates resolution changes to QWindowSystemInterface. |
| */ |
| |
| bool QWindowsScreenManager::handleScreenChanges() |
| { |
| // Look for changed monitors, add new ones |
| const WindowsScreenDataList newDataList = monitorData(); |
| const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen); |
| bool primaryScreenChanged = false; |
| for (const QWindowsScreenData &newData : newDataList) { |
| const int existingIndex = indexOfMonitor(m_screens, newData.name); |
| if (existingIndex != -1) { |
| m_screens.at(existingIndex)->handleChanges(newData); |
| if (existingIndex == 0) |
| primaryScreenChanged = true; |
| } else { |
| auto *newScreen = new QWindowsScreen(newData); |
| m_screens.push_back(newScreen); |
| QWindowSystemInterface::handleScreenAdded(newScreen, |
| newData.flags & QWindowsScreenData::PrimaryScreen); |
| qCDebug(lcQpaWindows) << "New Monitor: " << newData; |
| } // exists |
| } // for new screens. |
| // Remove deleted ones but keep main monitors if we get only the |
| // temporary lock screen to avoid window recreation (QTBUG-33062). |
| if (!lockScreen) { |
| for (int i = m_screens.size() - 1; i >= 0; --i) { |
| if (indexOfMonitor(newDataList, m_screens.at(i)->data().name) == -1) |
| removeScreen(i); |
| } // for existing screens |
| } // not lock screen |
| if (primaryScreenChanged) |
| QWindowsTheme::instance()->refreshFonts(); |
| return true; |
| } |
| |
| void QWindowsScreenManager::clearScreens() |
| { |
| // Delete screens in reverse order to avoid crash in case of multiple screens |
| while (!m_screens.isEmpty()) |
| QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); |
| } |
| |
| const QWindowsScreen *QWindowsScreenManager::screenAtDp(const QPoint &p) const |
| { |
| for (QWindowsScreen *scr : m_screens) { |
| if (scr->geometry().contains(p)) |
| return scr; |
| } |
| return nullptr; |
| } |
| |
| const QWindowsScreen *QWindowsScreenManager::screenForHwnd(HWND hwnd) const |
| { |
| HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); |
| if (hMonitor == nullptr) |
| return nullptr; |
| const auto it = |
| std::find_if(m_screens.cbegin(), m_screens.cend(), |
| [hMonitor](const QWindowsScreen *s) |
| { |
| return s->data().hMonitor == hMonitor |
| && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0; |
| }); |
| return it != m_screens.cend() ? *it : nullptr; |
| } |
| |
| QT_END_NAMESPACE |