| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #ifndef QT_NO_CURSOR |
| #include "qwindowscursor.h" |
| #include "qwindowscontext.h" |
| #include "qwindowswindow.h" |
| #include "qwindowsscreen.h" |
| |
| #include <QtGui/qbitmap.h> |
| #include <QtGui/qimage.h> |
| #include <QtGui/qbitmap.h> |
| #include <QtGui/qguiapplication.h> |
| #include <QtGui/qscreen.h> |
| #include <QtGui/private/qguiapplication_p.h> // getPixmapCursor() |
| #include <QtGui/private/qhighdpiscaling_p.h> |
| #include <QtCore/private/qwinregistry_p.h> |
| |
| #include <QtCore/qdebug.h> |
| #include <QtCore/qscopedpointer.h> |
| |
| static bool initResources() |
| { |
| #if QT_CONFIG(imageformat_png) |
| Q_INIT_RESOURCE(cursors); |
| #endif |
| return true; |
| } |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0); |
| Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap); |
| |
| /*! |
| \class QWindowsCursorCacheKey |
| \brief Cache key for storing values in a QHash with a QCursor as key. |
| |
| \internal |
| */ |
| |
| QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c) |
| : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0) |
| { |
| if (!bitmapCacheKey) { |
| Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull()); |
| Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull()); |
| bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey(); |
| maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey(); |
| } |
| } |
| |
| /*! |
| \class QWindowsCursor |
| \brief Platform cursor implementation |
| |
| Note that whereas under X11, a cursor can be set as a property of |
| a window, there is only a global SetCursor() function on Windows. |
| Each Window sets on the global cursor on receiving a Enter-event |
| as do the Window manager frames (resize/move handles). |
| |
| \internal |
| \sa QWindowsWindowCursor |
| */ |
| |
| HCURSOR QWindowsCursor::createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor) |
| { |
| HCURSOR cur = nullptr; |
| const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatioF(); |
| if (!qFuzzyCompare(pixmapScaleFactor, 1)) { |
| pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(), |
| Qt::KeepAspectRatio, Qt::SmoothTransformation); |
| } |
| QBitmap mask = pixmap.mask(); |
| if (mask.isNull()) { |
| mask = QBitmap(pixmap.size()); |
| mask.fill(Qt::color1); |
| } |
| |
| HBITMAP ic = qt_pixmapToWinHBITMAP(pixmap, /* HBitmapAlpha */ 2); |
| const HBITMAP im = qt_createIconMask(mask); |
| |
| ICONINFO ii; |
| ii.fIcon = 0; |
| ii.xHotspot = DWORD(qRound(hotSpot.x() * scaleFactor)); |
| ii.yHotspot = DWORD(qRound(hotSpot.y() * scaleFactor)); |
| ii.hbmMask = im; |
| ii.hbmColor = ic; |
| |
| cur = CreateIconIndirect(&ii); |
| |
| DeleteObject(ic); |
| DeleteObject(im); |
| return cur; |
| } |
| |
| // Create a cursor from image and mask of the format QImage::Format_Mono. |
| static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, |
| QPoint hotSpot = QPoint(-1, -1), |
| bool invb = false, bool invm = false) |
| { |
| const int width = bbits.width(); |
| const int height = bbits.height(); |
| if (hotSpot.x() < 0) |
| hotSpot.setX(width / 2); |
| if (hotSpot.y() < 0) |
| hotSpot.setY(height / 2); |
| const int n = qMax(1, width / 8); |
| QScopedArrayPointer<uchar> xBits(new uchar[height * n]); |
| QScopedArrayPointer<uchar> xMask(new uchar[height * n]); |
| int x = 0; |
| for (int i = 0; i < height; ++i) { |
| const uchar *bits = bbits.constScanLine(i); |
| const uchar *mask = mbits.constScanLine(i); |
| for (int j = 0; j < n; ++j) { |
| uchar b = bits[j]; |
| uchar m = mask[j]; |
| if (invb) |
| b ^= 0xff; |
| if (invm) |
| m ^= 0xff; |
| xBits[x] = ~m; |
| xMask[x] = b ^ m; |
| ++x; |
| } |
| } |
| return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height, |
| xBits.data(), xMask.data()); |
| } |
| |
| // Create a cursor from image and mask of the format QImage::Format_Mono. |
| static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1) |
| { |
| Q_ASSERT(cursor.shape() == Qt::BitmapCursor && !cursor.bitmap(Qt::ReturnByValue).isNull()); |
| QImage bbits = cursor.bitmap(Qt::ReturnByValue).toImage(); |
| QImage mbits = cursor.mask(Qt::ReturnByValue).toImage(); |
| scaleFactor /= bbits.devicePixelRatioF(); |
| if (!qFuzzyCompare(scaleFactor, 1)) { |
| const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize(); |
| bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
| mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
| } |
| bbits = bbits.convertToFormat(QImage::Format_Mono); |
| mbits = mbits.convertToFormat(QImage::Format_Mono); |
| const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); |
| const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); |
| return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm); |
| } |
| |
| static QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); } |
| |
| static QSize screenCursorSize(const QPlatformScreen *screen = nullptr) |
| { |
| const QSize primaryScreenCursorSize = systemCursorSize(); |
| if (screen) { |
| // Correct the size if the DPI value of the screen differs from |
| // that of the primary screen. |
| if (const QScreen *primaryQScreen = QGuiApplication::primaryScreen()) { |
| const QPlatformScreen *primaryScreen = primaryQScreen->handle(); |
| if (screen != primaryScreen) { |
| const qreal logicalDpi = screen->logicalDpi().first; |
| const qreal primaryScreenLogicalDpi = primaryScreen->logicalDpi().first; |
| if (!qFuzzyCompare(logicalDpi, primaryScreenLogicalDpi)) |
| return (QSizeF(primaryScreenCursorSize) * logicalDpi / primaryScreenLogicalDpi).toSize(); |
| } |
| } |
| } |
| return primaryScreenCursorSize; |
| } |
| |
| #if !QT_CONFIG(imageformat_png) |
| |
| static inline QSize standardCursorSize() { return QSize(32, 32); } |
| |
| // Create pixmap cursors from data and scale the image if the cursor size is |
| // higher than the standard 32. Note that bitmap cursors as produced by |
| // createBitmapCursor() only work for standard sizes (32,48,64...), which does |
| // not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting |
| // in a non-standard 24x24 size). |
| static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &screenCursorSize, |
| // The cursor size the bitmap is targeted for |
| const QSize &bitmapTargetCursorSize, |
| // The actual size of the bitmap data |
| int bitmapSize, const uchar *bits, |
| const uchar *maskBits) |
| { |
| QPixmap rawImage = QPixmap::fromImage(QBitmap::fromData(QSize(bitmapSize, bitmapSize), bits).toImage()); |
| rawImage.setMask(QBitmap::fromData(QSize(bitmapSize, bitmapSize), maskBits)); |
| |
| const qreal factor = qreal(screenCursorSize.width()) / qreal(bitmapTargetCursorSize.width()); |
| // Scale images if the cursor size is significantly different, starting with 150% where the system cursor |
| // size is 48. |
| if (qAbs(factor - 1.0) > 0.4) { |
| const QTransform transform = QTransform::fromScale(factor, factor); |
| rawImage = rawImage.transformed(transform, Qt::SmoothTransformation); |
| } |
| const QPoint hotSpot(rawImage.width() / 2, rawImage.height() / 2); |
| return QWindowsCursor::PixmapCursor(rawImage, hotSpot); |
| } |
| |
| QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape, |
| const QPlatformScreen *screen) |
| { |
| // Non-standard Windows cursors are created from bitmaps |
| static const uchar vsplit_bits[] = { |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, |
| 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, |
| 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, |
| 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, |
| 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, |
| 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| static const uchar vsplitm_bits[] = { |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, |
| 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, |
| 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, |
| 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, |
| 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, |
| 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, |
| 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, |
| 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| static const uchar hsplit_bits[] = { |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, |
| 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, |
| 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, |
| 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, |
| 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, |
| 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| static const uchar hsplitm_bits[] = { |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, |
| 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, |
| 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, |
| 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, |
| 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, |
| 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| static const uchar openhand_bits[] = { |
| 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, |
| 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, |
| 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; |
| static const uchar openhandm_bits[] = { |
| 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, |
| 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, |
| 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; |
| static const uchar closedhand_bits[] = { |
| 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, |
| 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, |
| 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; |
| static const uchar closedhandm_bits[] = { |
| 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, |
| 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, |
| 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; |
| |
| static const char * const moveDragCursorXpmC[] = { |
| "11 20 3 1", |
| ". c None", |
| "a c #FFFFFF", |
| "X c #000000", // X11 cursor is traditionally black |
| "aa.........", |
| "aXa........", |
| "aXXa.......", |
| "aXXXa......", |
| "aXXXXa.....", |
| "aXXXXXa....", |
| "aXXXXXXa...", |
| "aXXXXXXXa..", |
| "aXXXXXXXXa.", |
| "aXXXXXXXXXa", |
| "aXXXXXXaaaa", |
| "aXXXaXXa...", |
| "aXXaaXXa...", |
| "aXa..aXXa..", |
| "aa...aXXa..", |
| "a.....aXXa.", |
| "......aXXa.", |
| ".......aXXa", |
| ".......aXXa", |
| "........aa."}; |
| |
| static const char * const copyDragCursorXpmC[] = { |
| "24 30 3 1", |
| ". c None", |
| "a c #000000", |
| "X c #FFFFFF", |
| "XX......................", |
| "XaX.....................", |
| "XaaX....................", |
| "XaaaX...................", |
| "XaaaaX..................", |
| "XaaaaaX.................", |
| "XaaaaaaX................", |
| "XaaaaaaaX...............", |
| "XaaaaaaaaX..............", |
| "XaaaaaaaaaX.............", |
| "XaaaaaaXXXX.............", |
| "XaaaXaaX................", |
| "XaaXXaaX................", |
| "XaX..XaaX...............", |
| "XX...XaaX...............", |
| "X.....XaaX..............", |
| "......XaaX..............", |
| ".......XaaX.............", |
| ".......XaaX.............", |
| "........XX...aaaaaaaaaaa", |
| ".............aXXXXXXXXXa", |
| ".............aXXXXXXXXXa", |
| ".............aXXXXaXXXXa", |
| ".............aXXXXaXXXXa", |
| ".............aXXaaaaaXXa", |
| ".............aXXXXaXXXXa", |
| ".............aXXXXaXXXXa", |
| ".............aXXXXXXXXXa", |
| ".............aXXXXXXXXXa", |
| ".............aaaaaaaaaaa"}; |
| |
| static const char * const linkDragCursorXpmC[] = { |
| "24 30 3 1", |
| ". c None", |
| "a c #000000", |
| "X c #FFFFFF", |
| "XX......................", |
| "XaX.....................", |
| "XaaX....................", |
| "XaaaX...................", |
| "XaaaaX..................", |
| "XaaaaaX.................", |
| "XaaaaaaX................", |
| "XaaaaaaaX...............", |
| "XaaaaaaaaX..............", |
| "XaaaaaaaaaX.............", |
| "XaaaaaaXXXX.............", |
| "XaaaXaaX................", |
| "XaaXXaaX................", |
| "XaX..XaaX...............", |
| "XX...XaaX...............", |
| "X.....XaaX..............", |
| "......XaaX..............", |
| ".......XaaX.............", |
| ".......XaaX.............", |
| "........XX...aaaaaaaaaaa", |
| ".............aXXXXXXXXXa", |
| ".............aXXXaaaaXXa", |
| ".............aXXXXaaaXXa", |
| ".............aXXXaaaaXXa", |
| ".............aXXaaaXaXXa", |
| ".............aXXaaXXXXXa", |
| ".............aXXaXXXXXXa", |
| ".............aXXXaXXXXXa", |
| ".............aXXXXXXXXXa", |
| ".............aaaaaaaaaaa"}; |
| |
| switch (cursorShape) { |
| case Qt::SplitVCursor: |
| return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, vsplit_bits, vsplitm_bits); |
| case Qt::SplitHCursor: |
| return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, hsplit_bits, hsplitm_bits); |
| case Qt::OpenHandCursor: |
| return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, openhand_bits, openhandm_bits); |
| case Qt::ClosedHandCursor: |
| return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, closedhand_bits, closedhandm_bits); |
| case Qt::DragCopyCursor: |
| return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0)); |
| case Qt::DragMoveCursor: |
| return QWindowsCursor::PixmapCursor(QPixmap(moveDragCursorXpmC), QPoint(0, 0)); |
| case Qt::DragLinkCursor: |
| return QWindowsCursor::PixmapCursor(QPixmap(linkDragCursorXpmC), QPoint(0, 0)); |
| } |
| |
| return QWindowsCursor::PixmapCursor(); |
| } |
| #else // QT_NO_IMAGEFORMAT_PNG |
| struct QWindowsCustomPngCursor { |
| Qt::CursorShape shape; |
| int size; |
| const char *fileName; |
| int hotSpotX; |
| int hotSpotY; |
| }; |
| |
| QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen) |
| { |
| static const QWindowsCustomPngCursor pngCursors[] = { |
| { Qt::SplitVCursor, 32, "splitvcursor_32.png", 11, 11 }, |
| { Qt::SplitVCursor, 48, "splitvcursor_48.png", 16, 17 }, |
| { Qt::SplitVCursor, 64, "splitvcursor_64.png", 22, 22 }, |
| { Qt::SplitHCursor, 32, "splithcursor_32.png", 11, 11 }, |
| { Qt::SplitHCursor, 48, "splithcursor_48.png", 16, 17 }, |
| { Qt::SplitHCursor, 64, "splithcursor_64.png", 22, 22 }, |
| { Qt::OpenHandCursor, 32, "openhandcursor_32.png", 10, 12 }, |
| { Qt::OpenHandCursor, 48, "openhandcursor_48.png", 15, 16 }, |
| { Qt::OpenHandCursor, 64, "openhandcursor_64.png", 20, 24 }, |
| { Qt::ClosedHandCursor, 32, "closedhandcursor_32.png", 10, 12 }, |
| { Qt::ClosedHandCursor, 48, "closedhandcursor_48.png", 15, 16 }, |
| { Qt::ClosedHandCursor, 64, "closedhandcursor_64.png", 20, 24 }, |
| { Qt::DragCopyCursor, 32, "dragcopycursor_32.png", 0, 0 }, |
| { Qt::DragCopyCursor, 48, "dragcopycursor_48.png", 0, 0 }, |
| { Qt::DragCopyCursor, 64, "dragcopycursor_64.png", 0, 0 }, |
| { Qt::DragMoveCursor, 32, "dragmovecursor_32.png", 0, 0 }, |
| { Qt::DragMoveCursor, 48, "dragmovecursor_48.png", 0, 0 }, |
| { Qt::DragMoveCursor, 64, "dragmovecursor_64.png", 0, 0 }, |
| { Qt::DragLinkCursor, 32, "draglinkcursor_32.png", 0, 0 }, |
| { Qt::DragLinkCursor, 48, "draglinkcursor_48.png", 0, 0 }, |
| { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 } |
| }; |
| |
| const QSize cursorSize = screenCursorSize(screen); |
| const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]); |
| const QWindowsCustomPngCursor *bestFit = nullptr; |
| int sizeDelta = INT_MAX; |
| for (const QWindowsCustomPngCursor *s = pngCursors; s < sEnd; ++s) { |
| if (s->shape != cursorShape) |
| continue; |
| const int currentSizeDelta = qMax(s->size, cursorSize.width()) - qMin(s->size, cursorSize.width()); |
| if (currentSizeDelta < sizeDelta) { |
| bestFit = s; |
| if (currentSizeDelta == 0) |
| break; // Perfect match found |
| sizeDelta = currentSizeDelta; |
| } |
| } |
| |
| if (!bestFit) |
| return PixmapCursor(); |
| |
| const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") + |
| QString::fromLatin1(bestFit->fileName)); |
| return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY)); |
| } |
| #endif // !QT_NO_IMAGEFORMAT_PNG |
| |
| struct QWindowsStandardCursorMapping { |
| Qt::CursorShape shape; |
| LPCWSTR resource; |
| }; |
| |
| HCURSOR QWindowsCursor::createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen) |
| { |
| Q_ASSERT(cursorShape != Qt::BitmapCursor); |
| |
| static const QWindowsStandardCursorMapping standardCursors[] = { |
| { Qt::ArrowCursor, IDC_ARROW}, |
| { Qt::UpArrowCursor, IDC_UPARROW }, |
| { Qt::CrossCursor, IDC_CROSS }, |
| { Qt::WaitCursor, IDC_WAIT }, |
| { Qt::IBeamCursor, IDC_IBEAM }, |
| { Qt::SizeVerCursor, IDC_SIZENS }, |
| { Qt::SizeHorCursor, IDC_SIZEWE }, |
| { Qt::SizeBDiagCursor, IDC_SIZENESW }, |
| { Qt::SizeFDiagCursor, IDC_SIZENWSE }, |
| { Qt::SizeAllCursor, IDC_SIZEALL }, |
| { Qt::ForbiddenCursor, IDC_NO }, |
| { Qt::WhatsThisCursor, IDC_HELP }, |
| { Qt::BusyCursor, IDC_APPSTARTING }, |
| { Qt::PointingHandCursor, IDC_HAND } |
| }; |
| |
| switch (cursorShape) { |
| case Qt::BlankCursor: { |
| QImage blank = QImage(systemCursorSize(), QImage::Format_Mono); |
| blank.fill(0); // ignore color table |
| return createBitmapCursor(blank, blank); |
| } |
| case Qt::SplitVCursor: |
| case Qt::SplitHCursor: |
| case Qt::OpenHandCursor: |
| case Qt::ClosedHandCursor: |
| case Qt::DragCopyCursor: |
| case Qt::DragMoveCursor: |
| case Qt::DragLinkCursor: |
| return QWindowsCursor::createPixmapCursor(customCursor(cursorShape, screen)); |
| default: |
| break; |
| } |
| |
| // Load available standard cursors from resources |
| for (const QWindowsStandardCursorMapping &s : standardCursors) { |
| if (s.shape == cursorShape) { |
| return static_cast<HCURSOR>(LoadImage(nullptr, s.resource, IMAGE_CURSOR, |
| 0, 0, LR_DEFAULTSIZE | LR_SHARED)); |
| } |
| } |
| |
| qWarning("%s: Invalid cursor shape %d", __FUNCTION__, cursorShape); |
| return nullptr; |
| } |
| |
| /*! |
| \brief Return cached standard cursor resources or create new ones. |
| */ |
| |
| CursorHandlePtr QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) |
| { |
| StandardCursorCache::Iterator it = m_standardCursorCache.find(shape); |
| if (it == m_standardCursorCache.end()) { |
| if (const HCURSOR hc = QWindowsCursor::createCursorFromShape(shape, m_screen)) |
| it = m_standardCursorCache.insert(shape, CursorHandlePtr(new CursorHandle(hc))); |
| } |
| return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle); |
| } |
| |
| HCURSOR QWindowsCursor::m_overriddenCursor = nullptr; |
| HCURSOR QWindowsCursor::m_overrideCursor = nullptr; |
| |
| /*! |
| \brief Return cached pixmap cursor or create new one. |
| */ |
| |
| CursorHandlePtr QWindowsCursor::pixmapWindowCursor(const QCursor &c) |
| { |
| const QWindowsPixmapCursorCacheKey cacheKey(c); |
| PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey); |
| if (it == m_pixmapCursorCache.end()) { |
| if (m_pixmapCursorCache.size() > 50) { |
| // Prevent the cursor cache from growing indefinitely hitting GDI resource |
| // limits if new pixmap cursors are created repetitively by purging out |
| // all-noncurrent pixmap cursors (QTBUG-43515) |
| const HCURSOR currentCursor = GetCursor(); |
| for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) { |
| if (it.value()->handle() != currentCursor) |
| it = m_pixmapCursorCache.erase(it); |
| else |
| ++it; |
| } |
| } |
| const qreal scaleFactor = QHighDpiScaling::factor(m_screen); |
| const QPixmap pixmap = c.pixmap(); |
| const HCURSOR hc = pixmap.isNull() |
| ? createBitmapCursor(c, scaleFactor) |
| : QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot(), scaleFactor); |
| it = m_pixmapCursorCache.insert(cacheKey, CursorHandlePtr(new CursorHandle(hc))); |
| } |
| return it.value(); |
| } |
| |
| QWindowsCursor::QWindowsCursor(const QPlatformScreen *screen) |
| : m_screen(screen) |
| { |
| static const bool dummy = initResources(); |
| Q_UNUSED(dummy) |
| } |
| |
| inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor) |
| { |
| return cursor.shape() == Qt::BitmapCursor |
| ? pixmapWindowCursor(cursor) |
| : standardWindowCursor(cursor.shape()); |
| } |
| |
| /*! |
| \brief Set a cursor on a window. |
| |
| This is called frequently as the mouse moves over widgets in the window |
| (QLineEdits, etc). |
| */ |
| |
| void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) |
| { |
| QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(window); |
| if (!platformWindow) // Desktop/Foreign window. |
| return; |
| |
| if (!cursorIn) { |
| platformWindow->setCursor(CursorHandlePtr(new CursorHandle)); |
| return; |
| } |
| const CursorHandlePtr wcursor = cursorHandle(*cursorIn); |
| if (wcursor->handle()) { |
| platformWindow->setCursor(wcursor); |
| } else { |
| qWarning("%s: Unable to obtain system cursor for %d", |
| __FUNCTION__, cursorIn->shape()); |
| } |
| } |
| |
| // QTBUG-69637: Override cursors can get reset externally when moving across |
| // window borders. Enforce the cursor again (to be called from enter event). |
| void QWindowsCursor::enforceOverrideCursor() |
| { |
| if (hasOverrideCursor() && m_overrideCursor != GetCursor()) |
| SetCursor(m_overrideCursor); |
| } |
| |
| void QWindowsCursor::setOverrideCursor(const QCursor &cursor) |
| { |
| const CursorHandlePtr wcursor = cursorHandle(cursor); |
| if (const auto overrideCursor = wcursor->handle()) { |
| m_overrideCursor = overrideCursor; |
| const HCURSOR previousCursor = SetCursor(overrideCursor); |
| if (m_overriddenCursor == nullptr) |
| m_overriddenCursor = previousCursor; |
| } else { |
| qWarning("%s: Unable to obtain system cursor for %d", |
| __FUNCTION__, cursor.shape()); |
| } |
| } |
| |
| void QWindowsCursor::clearOverrideCursor() |
| { |
| if (m_overriddenCursor) { |
| SetCursor(m_overriddenCursor); |
| m_overriddenCursor = m_overrideCursor = nullptr; |
| } |
| } |
| |
| QPoint QWindowsCursor::mousePosition() |
| { |
| POINT p; |
| GetCursorPos(&p); |
| return QPoint(p.x, p.y); |
| } |
| |
| QWindowsCursor::State QWindowsCursor::cursorState() |
| { |
| enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; // Windows 8: CURSOR_SUPPRESSED |
| CURSORINFO cursorInfo; |
| cursorInfo.cbSize = sizeof(CURSORINFO); |
| if (GetCursorInfo(&cursorInfo)) { |
| if (cursorInfo.flags & cursorShowing) |
| return State::Showing; |
| if (cursorInfo.flags & cursorSuppressed) |
| return State::Suppressed; |
| } |
| return State::Hidden; |
| } |
| |
| QPoint QWindowsCursor::pos() const |
| { |
| return mousePosition(); |
| } |
| |
| void QWindowsCursor::setPos(const QPoint &pos) |
| { |
| SetCursorPos(pos.x() , pos.y()); |
| } |
| |
| /* |
| The standard size is 32x32, even though the cursor is actually just |
| 16 pixels large. If a large cursor is set in the accessibility settings, |
| then the cursor increases with 8 pixels for each step. |
| */ |
| QSize QWindowsCursor::size() const |
| { |
| const QPair<DWORD,bool> cursorSizeSetting = |
| QWinRegistryKey(HKEY_CURRENT_USER, LR"(Control Panel\Cursors)") |
| .dwordValue(L"CursorBaseSize"); |
| const int baseSize = screenCursorSize(m_screen).width() / 2; |
| if (!cursorSizeSetting.second) |
| return QSize(baseSize / 2, baseSize / 2); |
| |
| // The registry values are dpi-independent, so we need to scale the result. |
| int cursorSizeValue = cursorSizeSetting.first * m_screen->logicalDpi().first |
| / m_screen->logicalBaseDpi().first; |
| |
| // map from registry value 32-256 to 0-14, and from there to pixels |
| cursorSizeValue = (cursorSizeValue - 2 * baseSize) / baseSize; |
| const int cursorSize = baseSize + cursorSizeValue * (baseSize / 2); |
| return QSize(cursorSize, cursorSize); |
| } |
| |
| QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const |
| { |
| switch (action) { |
| case Qt::CopyAction: |
| if (m_copyDragCursor.isNull()) |
| m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor, m_screen).pixmap; |
| return m_copyDragCursor; |
| case Qt::TargetMoveAction: |
| case Qt::MoveAction: |
| if (m_moveDragCursor.isNull()) |
| m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor, m_screen).pixmap; |
| return m_moveDragCursor; |
| case Qt::LinkAction: |
| if (m_linkDragCursor.isNull()) |
| m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor, m_screen).pixmap; |
| return m_linkDragCursor; |
| default: |
| break; |
| } |
| |
| static const char * const ignoreDragCursorXpmC[] = { |
| "24 30 3 1", |
| ". c None", |
| "a c #000000", |
| "X c #FFFFFF", |
| "aa......................", |
| "aXa.....................", |
| "aXXa....................", |
| "aXXXa...................", |
| "aXXXXa..................", |
| "aXXXXXa.................", |
| "aXXXXXXa................", |
| "aXXXXXXXa...............", |
| "aXXXXXXXXa..............", |
| "aXXXXXXXXXa.............", |
| "aXXXXXXaaaa.............", |
| "aXXXaXXa................", |
| "aXXaaXXa................", |
| "aXa..aXXa...............", |
| "aa...aXXa...............", |
| "a.....aXXa..............", |
| "......aXXa.....XXXX.....", |
| ".......aXXa..XXaaaaXX...", |
| ".......aXXa.XaaaaaaaaX..", |
| "........aa.XaaaXXXXaaaX.", |
| "...........XaaaaX..XaaX.", |
| "..........XaaXaaaX..XaaX", |
| "..........XaaXXaaaX.XaaX", |
| "..........XaaX.XaaaXXaaX", |
| "..........XaaX..XaaaXaaX", |
| "...........XaaX..XaaaaX.", |
| "...........XaaaXXXXaaaX.", |
| "............XaaaaaaaaX..", |
| ".............XXaaaaXX...", |
| "...............XXXX....."}; |
| |
| if (m_ignoreDragCursor.isNull()) { |
| HCURSOR cursor = LoadCursor(nullptr, IDC_NO); |
| ICONINFO iconInfo = {0, 0, 0, nullptr, nullptr}; |
| GetIconInfo(cursor, &iconInfo); |
| BITMAP bmColor = {0, 0, 0, 0, 0, 0, nullptr}; |
| |
| if (iconInfo.hbmColor |
| && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor) |
| && bmColor.bmWidth == bmColor.bmWidthBytes / 4) { |
| const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes; |
| auto *colorBits = new uchar[colorBitsLength]; |
| GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits); |
| const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight, |
| bmColor.bmWidthBytes, QImage::Format_ARGB32); |
| |
| m_ignoreDragCursor = QPixmap::fromImage(colorImage); |
| delete [] colorBits; |
| } else { |
| m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC); |
| } |
| |
| DeleteObject(iconInfo.hbmMask); |
| DeleteObject(iconInfo.hbmColor); |
| DestroyCursor(cursor); |
| } |
| return m_ignoreDragCursor; |
| } |
| |
| HCURSOR QWindowsCursor::hCursor(const QCursor &c) const |
| { |
| const Qt::CursorShape shape = c.shape(); |
| if (shape == Qt::BitmapCursor) { |
| const auto pit = m_pixmapCursorCache.constFind(QWindowsPixmapCursorCacheKey(c)); |
| if (pit != m_pixmapCursorCache.constEnd()) |
| return pit.value()->handle(); |
| } else { |
| const auto sit = m_standardCursorCache.constFind(shape); |
| if (sit != m_standardCursorCache.constEnd()) |
| return sit.value()->handle(); |
| } |
| return HCURSOR(nullptr); |
| } |
| |
| /*! |
| \class QWindowsWindowCursor |
| \brief Per-Window cursor. Contains a QCursor and manages its associated system |
| cursor handle resource. |
| |
| \internal |
| \sa QWindowsCursor |
| */ |
| |
| QT_END_NAMESPACE |
| |
| #endif // !QT_NO_CURSOR |