1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef  QT_NO_CURSOR
41 #include "qwindowscursor.h"
42 #include "qwindowscontext.h"
43 #include "qwindowswindow.h"
44 #include "qwindowsscreen.h"
45 
46 #include <QtGui/qbitmap.h>
47 #include <QtGui/qimage.h>
48 #include <QtGui/qbitmap.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qscreen.h>
51 #include <QtGui/private/qguiapplication_p.h> // getPixmapCursor()
52 #include <QtGui/private/qhighdpiscaling_p.h>
53 #include <QtCore/private/qwinregistry_p.h>
54 
55 #include <QtCore/qdebug.h>
56 #include <QtCore/qscopedpointer.h>
57 
initResources()58 static bool initResources()
59 {
60 #if QT_CONFIG(imageformat_png)
61     Q_INIT_RESOURCE(cursors);
62 #endif
63     return true;
64 }
65 
66 QT_BEGIN_NAMESPACE
67 
68 Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
69 Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap);
70 
71 /*!
72     \class QWindowsCursorCacheKey
73     \brief Cache key for storing values in a QHash with a QCursor as key.
74 
75     \internal
76 */
77 
QWindowsPixmapCursorCacheKey(const QCursor & c)78 QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c)
79     : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0)
80 {
81     if (!bitmapCacheKey) {
82         Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull());
83         Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull());
84         bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey();
85         maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey();
86     }
87 }
88 
89 /*!
90     \class QWindowsCursor
91     \brief Platform cursor implementation
92 
93     Note that whereas under X11, a cursor can be set as a property of
94     a window, there is only a global SetCursor() function on Windows.
95     Each Window sets on the global cursor on receiving a Enter-event
96     as do the Window manager frames (resize/move handles).
97 
98     \internal
99     \sa QWindowsWindowCursor
100 */
101 
createPixmapCursor(QPixmap pixmap,const QPoint & hotSpot,qreal scaleFactor)102 HCURSOR QWindowsCursor::createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor)
103 {
104     HCURSOR cur = nullptr;
105     const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatioF();
106     if (!qFuzzyCompare(pixmapScaleFactor, 1)) {
107         pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(),
108                                Qt::KeepAspectRatio, Qt::SmoothTransformation);
109     }
110     QBitmap mask = pixmap.mask();
111     if (mask.isNull()) {
112         mask = QBitmap(pixmap.size());
113         mask.fill(Qt::color1);
114     }
115 
116     HBITMAP ic = qt_pixmapToWinHBITMAP(pixmap, /* HBitmapAlpha */ 2);
117     const HBITMAP im = qt_createIconMask(mask);
118 
119     ICONINFO ii;
120     ii.fIcon     = 0;
121     ii.xHotspot  = DWORD(qRound(hotSpot.x() * scaleFactor));
122     ii.yHotspot  = DWORD(qRound(hotSpot.y() * scaleFactor));
123     ii.hbmMask   = im;
124     ii.hbmColor  = ic;
125 
126     cur = CreateIconIndirect(&ii);
127 
128     DeleteObject(ic);
129     DeleteObject(im);
130     return cur;
131 }
132 
133 // Create a cursor from image and mask of the format QImage::Format_Mono.
createBitmapCursor(const QImage & bbits,const QImage & mbits,QPoint hotSpot=QPoint (-1,-1),bool invb=false,bool invm=false)134 static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits,
135                                   QPoint hotSpot = QPoint(-1, -1),
136                                   bool invb = false, bool invm = false)
137 {
138     const int width = bbits.width();
139     const int height = bbits.height();
140     if (hotSpot.x() < 0)
141         hotSpot.setX(width / 2);
142     if (hotSpot.y() < 0)
143         hotSpot.setY(height / 2);
144     const int n = qMax(1, width / 8);
145     QScopedArrayPointer<uchar> xBits(new uchar[height * n]);
146     QScopedArrayPointer<uchar> xMask(new uchar[height * n]);
147     int x = 0;
148     for (int i = 0; i < height; ++i) {
149         const uchar *bits = bbits.constScanLine(i);
150         const uchar *mask = mbits.constScanLine(i);
151         for (int j = 0; j < n; ++j) {
152             uchar b = bits[j];
153             uchar m = mask[j];
154             if (invb)
155                 b ^= 0xff;
156             if (invm)
157                 m ^= 0xff;
158             xBits[x] = ~m;
159             xMask[x] = b ^ m;
160             ++x;
161         }
162     }
163     return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height,
164                         xBits.data(), xMask.data());
165 }
166 
167 // Create a cursor from image and mask of the format QImage::Format_Mono.
createBitmapCursor(const QCursor & cursor,qreal scaleFactor=1)168 static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1)
169 {
170     Q_ASSERT(cursor.shape() == Qt::BitmapCursor && !cursor.bitmap(Qt::ReturnByValue).isNull());
171     QImage bbits = cursor.bitmap(Qt::ReturnByValue).toImage();
172     QImage mbits = cursor.mask(Qt::ReturnByValue).toImage();
173     scaleFactor /= bbits.devicePixelRatioF();
174     if (!qFuzzyCompare(scaleFactor, 1)) {
175         const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize();
176         bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
177         mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
178     }
179     bbits = bbits.convertToFormat(QImage::Format_Mono);
180     mbits = mbits.convertToFormat(QImage::Format_Mono);
181     const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
182     const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
183     return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm);
184 }
185 
systemCursorSize()186 static QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); }
187 
screenCursorSize(const QPlatformScreen * screen=nullptr)188 static QSize screenCursorSize(const QPlatformScreen *screen = nullptr)
189 {
190     const QSize primaryScreenCursorSize = systemCursorSize();
191     if (screen) {
192         // Correct the size if the DPI value of the screen differs from
193         // that of the primary screen.
194         if (const QScreen *primaryQScreen = QGuiApplication::primaryScreen()) {
195             const QPlatformScreen *primaryScreen = primaryQScreen->handle();
196             if (screen != primaryScreen) {
197                 const qreal logicalDpi = screen->logicalDpi().first;
198                 const qreal primaryScreenLogicalDpi = primaryScreen->logicalDpi().first;
199                 if (!qFuzzyCompare(logicalDpi, primaryScreenLogicalDpi))
200                     return (QSizeF(primaryScreenCursorSize) * logicalDpi / primaryScreenLogicalDpi).toSize();
201             }
202         }
203     }
204     return primaryScreenCursorSize;
205 }
206 
207 #if !QT_CONFIG(imageformat_png)
208 
standardCursorSize()209 static inline QSize standardCursorSize() { return QSize(32, 32); }
210 
211 // Create pixmap cursors from data and scale the image if the cursor size is
212 // higher than the standard 32. Note that bitmap cursors as produced by
213 // createBitmapCursor() only work for standard sizes (32,48,64...), which does
214 // not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting
215 // in a non-standard 24x24 size).
createPixmapCursorFromData(const QSize & screenCursorSize,const QSize & bitmapTargetCursorSize,int bitmapSize,const uchar * bits,const uchar * maskBits)216 static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &screenCursorSize,
217                                           // The cursor size the bitmap is targeted for
218                                           const QSize &bitmapTargetCursorSize,
219                                           // The actual size of the bitmap data
220                                           int bitmapSize, const uchar *bits,
221                                           const uchar *maskBits)
222 {
223     QPixmap rawImage = QPixmap::fromImage(QBitmap::fromData(QSize(bitmapSize, bitmapSize), bits).toImage());
224     rawImage.setMask(QBitmap::fromData(QSize(bitmapSize, bitmapSize), maskBits));
225 
226     const qreal factor = qreal(screenCursorSize.width()) / qreal(bitmapTargetCursorSize.width());
227     // Scale images if the cursor size is significantly different, starting with 150% where the system cursor
228     // size is 48.
229     if (qAbs(factor - 1.0) > 0.4) {
230         const QTransform transform = QTransform::fromScale(factor, factor);
231         rawImage = rawImage.transformed(transform, Qt::SmoothTransformation);
232     }
233     const QPoint hotSpot(rawImage.width() / 2, rawImage.height() / 2);
234     return QWindowsCursor::PixmapCursor(rawImage, hotSpot);
235 }
236 
customCursor(Qt::CursorShape cursorShape,const QPlatformScreen * screen)237 QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape,
238                                                           const QPlatformScreen *screen)
239 {
240     // Non-standard Windows cursors are created from bitmaps
241     static const uchar vsplit_bits[] = {
242         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244         0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
245         0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
246         0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
247         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
248         0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
249         0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
250         0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
253     static const uchar vsplitm_bits[] = {
254         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
256         0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
257         0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
258         0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
259         0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
260         0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
261         0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
262         0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
263         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
265     static const uchar hsplit_bits[] = {
266         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268         0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
269         0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
270         0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
271         0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
272         0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
273         0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
274         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
277     static const uchar hsplitm_bits[] = {
278         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280         0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
281         0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
282         0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
283         0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
284         0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
285         0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
286         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
289    static const uchar openhand_bits[] = {
290         0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
291         0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
292         0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
293     static const uchar openhandm_bits[] = {
294        0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
295        0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
296        0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
297     static const uchar closedhand_bits[] = {
298         0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
299         0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
300         0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
301     static const uchar closedhandm_bits[] = {
302         0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
303         0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
304         0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
305 
306     static const char * const moveDragCursorXpmC[] = {
307     "11 20 3 1",
308     ".        c None",
309     "a        c #FFFFFF",
310     "X        c #000000", // X11 cursor is traditionally black
311     "aa.........",
312     "aXa........",
313     "aXXa.......",
314     "aXXXa......",
315     "aXXXXa.....",
316     "aXXXXXa....",
317     "aXXXXXXa...",
318     "aXXXXXXXa..",
319     "aXXXXXXXXa.",
320     "aXXXXXXXXXa",
321     "aXXXXXXaaaa",
322     "aXXXaXXa...",
323     "aXXaaXXa...",
324     "aXa..aXXa..",
325     "aa...aXXa..",
326     "a.....aXXa.",
327     "......aXXa.",
328     ".......aXXa",
329     ".......aXXa",
330     "........aa."};
331 
332     static const char * const copyDragCursorXpmC[] = {
333     "24 30 3 1",
334     ".        c None",
335     "a        c #000000",
336     "X        c #FFFFFF",
337     "XX......................",
338     "XaX.....................",
339     "XaaX....................",
340     "XaaaX...................",
341     "XaaaaX..................",
342     "XaaaaaX.................",
343     "XaaaaaaX................",
344     "XaaaaaaaX...............",
345     "XaaaaaaaaX..............",
346     "XaaaaaaaaaX.............",
347     "XaaaaaaXXXX.............",
348     "XaaaXaaX................",
349     "XaaXXaaX................",
350     "XaX..XaaX...............",
351     "XX...XaaX...............",
352     "X.....XaaX..............",
353     "......XaaX..............",
354     ".......XaaX.............",
355     ".......XaaX.............",
356     "........XX...aaaaaaaaaaa",
357     ".............aXXXXXXXXXa",
358     ".............aXXXXXXXXXa",
359     ".............aXXXXaXXXXa",
360     ".............aXXXXaXXXXa",
361     ".............aXXaaaaaXXa",
362     ".............aXXXXaXXXXa",
363     ".............aXXXXaXXXXa",
364     ".............aXXXXXXXXXa",
365     ".............aXXXXXXXXXa",
366     ".............aaaaaaaaaaa"};
367 
368     static const char * const linkDragCursorXpmC[] = {
369     "24 30 3 1",
370     ".        c None",
371     "a        c #000000",
372     "X        c #FFFFFF",
373     "XX......................",
374     "XaX.....................",
375     "XaaX....................",
376     "XaaaX...................",
377     "XaaaaX..................",
378     "XaaaaaX.................",
379     "XaaaaaaX................",
380     "XaaaaaaaX...............",
381     "XaaaaaaaaX..............",
382     "XaaaaaaaaaX.............",
383     "XaaaaaaXXXX.............",
384     "XaaaXaaX................",
385     "XaaXXaaX................",
386     "XaX..XaaX...............",
387     "XX...XaaX...............",
388     "X.....XaaX..............",
389     "......XaaX..............",
390     ".......XaaX.............",
391     ".......XaaX.............",
392     "........XX...aaaaaaaaaaa",
393     ".............aXXXXXXXXXa",
394     ".............aXXXaaaaXXa",
395     ".............aXXXXaaaXXa",
396     ".............aXXXaaaaXXa",
397     ".............aXXaaaXaXXa",
398     ".............aXXaaXXXXXa",
399     ".............aXXaXXXXXXa",
400     ".............aXXXaXXXXXa",
401     ".............aXXXXXXXXXa",
402     ".............aaaaaaaaaaa"};
403 
404     switch (cursorShape) {
405     case Qt::SplitVCursor:
406         return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, vsplit_bits, vsplitm_bits);
407     case Qt::SplitHCursor:
408         return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, hsplit_bits, hsplitm_bits);
409     case Qt::OpenHandCursor:
410         return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, openhand_bits, openhandm_bits);
411     case Qt::ClosedHandCursor:
412         return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, closedhand_bits, closedhandm_bits);
413     case Qt::DragCopyCursor:
414         return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0));
415     case Qt::DragMoveCursor:
416         return QWindowsCursor::PixmapCursor(QPixmap(moveDragCursorXpmC), QPoint(0, 0));
417     case Qt::DragLinkCursor:
418         return QWindowsCursor::PixmapCursor(QPixmap(linkDragCursorXpmC), QPoint(0, 0));
419     }
420 
421     return QWindowsCursor::PixmapCursor();
422 }
423 #else // QT_NO_IMAGEFORMAT_PNG
424 struct QWindowsCustomPngCursor {
425     Qt::CursorShape shape;
426     int size;
427     const char *fileName;
428     int hotSpotX;
429     int hotSpotY;
430 };
431 
customCursor(Qt::CursorShape cursorShape,const QPlatformScreen * screen)432 QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen)
433 {
434     static const QWindowsCustomPngCursor pngCursors[] = {
435         { Qt::SplitVCursor, 32, "splitvcursor_32.png", 11, 11 },
436         { Qt::SplitVCursor, 48, "splitvcursor_48.png", 16, 17 },
437         { Qt::SplitVCursor, 64, "splitvcursor_64.png", 22, 22 },
438         { Qt::SplitHCursor, 32, "splithcursor_32.png", 11, 11 },
439         { Qt::SplitHCursor, 48, "splithcursor_48.png", 16, 17 },
440         { Qt::SplitHCursor, 64, "splithcursor_64.png", 22, 22 },
441         { Qt::OpenHandCursor, 32, "openhandcursor_32.png", 10, 12 },
442         { Qt::OpenHandCursor, 48, "openhandcursor_48.png", 15, 16 },
443         { Qt::OpenHandCursor, 64, "openhandcursor_64.png", 20, 24 },
444         { Qt::ClosedHandCursor, 32, "closedhandcursor_32.png", 10, 12 },
445         { Qt::ClosedHandCursor, 48, "closedhandcursor_48.png", 15, 16 },
446         { Qt::ClosedHandCursor, 64, "closedhandcursor_64.png", 20, 24 },
447         { Qt::DragCopyCursor, 32, "dragcopycursor_32.png", 0, 0 },
448         { Qt::DragCopyCursor, 48, "dragcopycursor_48.png", 0, 0 },
449         { Qt::DragCopyCursor, 64, "dragcopycursor_64.png", 0, 0 },
450         { Qt::DragMoveCursor, 32, "dragmovecursor_32.png", 0, 0 },
451         { Qt::DragMoveCursor, 48, "dragmovecursor_48.png", 0, 0 },
452         { Qt::DragMoveCursor, 64, "dragmovecursor_64.png", 0, 0 },
453         { Qt::DragLinkCursor, 32, "draglinkcursor_32.png", 0, 0 },
454         { Qt::DragLinkCursor, 48, "draglinkcursor_48.png", 0, 0 },
455         { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 }
456     };
457 
458     const QSize cursorSize = screenCursorSize(screen);
459     const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]);
460     const QWindowsCustomPngCursor *bestFit = nullptr;
461     int sizeDelta = INT_MAX;
462     for (const QWindowsCustomPngCursor *s = pngCursors; s < sEnd; ++s) {
463         if (s->shape != cursorShape)
464             continue;
465         const int currentSizeDelta = qMax(s->size, cursorSize.width()) - qMin(s->size, cursorSize.width());
466         if (currentSizeDelta < sizeDelta) {
467             bestFit = s;
468             if (currentSizeDelta == 0)
469                 break; // Perfect match found
470             sizeDelta = currentSizeDelta;
471         }
472     }
473 
474     if (!bestFit)
475         return PixmapCursor();
476 
477     const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") +
478                            QString::fromLatin1(bestFit->fileName));
479     return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY));
480 }
481 #endif // !QT_NO_IMAGEFORMAT_PNG
482 
483 struct QWindowsStandardCursorMapping {
484     Qt::CursorShape shape;
485     LPCWSTR resource;
486 };
487 
createCursorFromShape(Qt::CursorShape cursorShape,const QPlatformScreen * screen)488 HCURSOR QWindowsCursor::createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen)
489 {
490     Q_ASSERT(cursorShape != Qt::BitmapCursor);
491 
492     static const QWindowsStandardCursorMapping standardCursors[] = {
493         { Qt::ArrowCursor, IDC_ARROW},
494         { Qt::UpArrowCursor, IDC_UPARROW },
495         { Qt::CrossCursor, IDC_CROSS },
496         { Qt::WaitCursor, IDC_WAIT },
497         { Qt::IBeamCursor, IDC_IBEAM },
498         { Qt::SizeVerCursor, IDC_SIZENS },
499         { Qt::SizeHorCursor, IDC_SIZEWE },
500         { Qt::SizeBDiagCursor, IDC_SIZENESW },
501         { Qt::SizeFDiagCursor, IDC_SIZENWSE },
502         { Qt::SizeAllCursor, IDC_SIZEALL },
503         { Qt::ForbiddenCursor, IDC_NO },
504         { Qt::WhatsThisCursor, IDC_HELP },
505         { Qt::BusyCursor, IDC_APPSTARTING },
506         { Qt::PointingHandCursor, IDC_HAND }
507     };
508 
509     switch (cursorShape) {
510     case Qt::BlankCursor: {
511         QImage blank = QImage(systemCursorSize(), QImage::Format_Mono);
512         blank.fill(0); // ignore color table
513         return createBitmapCursor(blank, blank);
514     }
515     case Qt::SplitVCursor:
516     case Qt::SplitHCursor:
517     case Qt::OpenHandCursor:
518     case Qt::ClosedHandCursor:
519     case Qt::DragCopyCursor:
520     case Qt::DragMoveCursor:
521     case Qt::DragLinkCursor:
522         return QWindowsCursor::createPixmapCursor(customCursor(cursorShape, screen));
523     default:
524         break;
525     }
526 
527     // Load available standard cursors from resources
528     for (const QWindowsStandardCursorMapping &s : standardCursors) {
529         if (s.shape == cursorShape) {
530             return static_cast<HCURSOR>(LoadImage(nullptr, s.resource, IMAGE_CURSOR,
531                                                   0, 0, LR_DEFAULTSIZE | LR_SHARED));
532         }
533     }
534 
535     qWarning("%s: Invalid cursor shape %d", __FUNCTION__, cursorShape);
536     return nullptr;
537 }
538 
539 /*!
540     \brief Return cached standard cursor resources or create new ones.
541 */
542 
standardWindowCursor(Qt::CursorShape shape)543 CursorHandlePtr QWindowsCursor::standardWindowCursor(Qt::CursorShape shape)
544 {
545     StandardCursorCache::Iterator it = m_standardCursorCache.find(shape);
546     if (it == m_standardCursorCache.end()) {
547         if (const HCURSOR hc = QWindowsCursor::createCursorFromShape(shape, m_screen))
548             it = m_standardCursorCache.insert(shape, CursorHandlePtr(new CursorHandle(hc)));
549     }
550     return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle);
551 }
552 
553 HCURSOR QWindowsCursor::m_overriddenCursor = nullptr;
554 HCURSOR QWindowsCursor::m_overrideCursor = nullptr;
555 
556 /*!
557     \brief Return cached pixmap cursor or create new one.
558 */
559 
pixmapWindowCursor(const QCursor & c)560 CursorHandlePtr QWindowsCursor::pixmapWindowCursor(const QCursor &c)
561 {
562     const QWindowsPixmapCursorCacheKey cacheKey(c);
563     PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey);
564     if (it == m_pixmapCursorCache.end()) {
565         if (m_pixmapCursorCache.size() > 50) {
566             // Prevent the cursor cache from growing indefinitely hitting GDI resource
567             // limits if new pixmap cursors are created repetitively by purging out
568             // all-noncurrent pixmap cursors (QTBUG-43515)
569             const HCURSOR currentCursor = GetCursor();
570             for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) {
571                 if (it.value()->handle() != currentCursor)
572                     it = m_pixmapCursorCache.erase(it);
573                 else
574                     ++it;
575             }
576         }
577         const qreal scaleFactor = QHighDpiScaling::factor(m_screen);
578         const QPixmap pixmap = c.pixmap();
579         const HCURSOR hc = pixmap.isNull()
580             ? createBitmapCursor(c, scaleFactor)
581             : QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot(), scaleFactor);
582         it = m_pixmapCursorCache.insert(cacheKey, CursorHandlePtr(new CursorHandle(hc)));
583     }
584     return it.value();
585 }
586 
QWindowsCursor(const QPlatformScreen * screen)587 QWindowsCursor::QWindowsCursor(const QPlatformScreen *screen)
588     : m_screen(screen)
589 {
590     static const bool dummy = initResources();
591     Q_UNUSED(dummy)
592 }
593 
cursorHandle(const QCursor & cursor)594 inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor)
595 {
596     return cursor.shape() == Qt::BitmapCursor
597         ? pixmapWindowCursor(cursor)
598         : standardWindowCursor(cursor.shape());
599 }
600 
601 /*!
602     \brief Set a cursor on a window.
603 
604     This is called frequently as the mouse moves over widgets in the window
605     (QLineEdits, etc).
606 */
607 
changeCursor(QCursor * cursorIn,QWindow * window)608 void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window)
609 {
610     QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(window);
611     if (!platformWindow) // Desktop/Foreign window.
612         return;
613 
614     if (!cursorIn) {
615         platformWindow->setCursor(CursorHandlePtr(new CursorHandle));
616         return;
617     }
618     const CursorHandlePtr wcursor = cursorHandle(*cursorIn);
619     if (wcursor->handle()) {
620         platformWindow->setCursor(wcursor);
621     } else {
622         qWarning("%s: Unable to obtain system cursor for %d",
623                  __FUNCTION__, cursorIn->shape());
624     }
625 }
626 
627 // QTBUG-69637: Override cursors can get reset externally when moving across
628 // window borders. Enforce the cursor again (to be called from enter event).
enforceOverrideCursor()629 void QWindowsCursor::enforceOverrideCursor()
630 {
631     if (hasOverrideCursor() && m_overrideCursor != GetCursor())
632         SetCursor(m_overrideCursor);
633 }
634 
setOverrideCursor(const QCursor & cursor)635 void QWindowsCursor::setOverrideCursor(const QCursor &cursor)
636 {
637     const CursorHandlePtr wcursor = cursorHandle(cursor);
638     if (const auto overrideCursor = wcursor->handle()) {
639         m_overrideCursor = overrideCursor;
640         const HCURSOR previousCursor = SetCursor(overrideCursor);
641         if (m_overriddenCursor == nullptr)
642             m_overriddenCursor = previousCursor;
643     } else {
644         qWarning("%s: Unable to obtain system cursor for %d",
645                  __FUNCTION__, cursor.shape());
646     }
647 }
648 
clearOverrideCursor()649 void QWindowsCursor::clearOverrideCursor()
650 {
651     if (m_overriddenCursor) {
652         SetCursor(m_overriddenCursor);
653         m_overriddenCursor = m_overrideCursor = nullptr;
654     }
655 }
656 
mousePosition()657 QPoint QWindowsCursor::mousePosition()
658 {
659     POINT p;
660     GetCursorPos(&p);
661     return QPoint(p.x, p.y);
662 }
663 
cursorState()664 QWindowsCursor::State QWindowsCursor::cursorState()
665 {
666     enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; // Windows 8: CURSOR_SUPPRESSED
667     CURSORINFO cursorInfo;
668     cursorInfo.cbSize = sizeof(CURSORINFO);
669     if (GetCursorInfo(&cursorInfo)) {
670         if (cursorInfo.flags & cursorShowing)
671             return State::Showing;
672         if (cursorInfo.flags & cursorSuppressed)
673             return State::Suppressed;
674     }
675     return State::Hidden;
676 }
677 
pos() const678 QPoint QWindowsCursor::pos() const
679 {
680     return mousePosition();
681 }
682 
setPos(const QPoint & pos)683 void QWindowsCursor::setPos(const QPoint &pos)
684 {
685     SetCursorPos(pos.x() , pos.y());
686 }
687 
688 /*
689     The standard size is 32x32, even though the cursor is actually just
690     16 pixels large. If a large cursor is set in the accessibility settings,
691     then the cursor increases with 8 pixels for each step.
692 */
size() const693 QSize QWindowsCursor::size() const
694 {
695     const QPair<DWORD,bool> cursorSizeSetting =
696         QWinRegistryKey(HKEY_CURRENT_USER, LR"(Control Panel\Cursors)")
697                        .dwordValue(L"CursorBaseSize");
698     const int baseSize = screenCursorSize(m_screen).width() / 2;
699     if (!cursorSizeSetting.second)
700         return QSize(baseSize / 2, baseSize / 2);
701 
702     // The registry values are dpi-independent, so we need to scale the result.
703     int cursorSizeValue = cursorSizeSetting.first * m_screen->logicalDpi().first
704                                                   / m_screen->logicalBaseDpi().first;
705 
706     // map from registry value 32-256 to 0-14, and from there to pixels
707     cursorSizeValue = (cursorSizeValue - 2 * baseSize) / baseSize;
708     const int cursorSize = baseSize + cursorSizeValue * (baseSize / 2);
709     return QSize(cursorSize, cursorSize);
710 }
711 
dragDefaultCursor(Qt::DropAction action) const712 QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const
713 {
714     switch (action) {
715     case Qt::CopyAction:
716         if (m_copyDragCursor.isNull())
717             m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor, m_screen).pixmap;
718         return m_copyDragCursor;
719     case Qt::TargetMoveAction:
720     case Qt::MoveAction:
721         if (m_moveDragCursor.isNull())
722             m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor, m_screen).pixmap;
723         return m_moveDragCursor;
724     case Qt::LinkAction:
725         if (m_linkDragCursor.isNull())
726             m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor, m_screen).pixmap;
727         return m_linkDragCursor;
728     default:
729         break;
730     }
731 
732     static const char * const ignoreDragCursorXpmC[] = {
733     "24 30 3 1",
734     ".        c None",
735     "a        c #000000",
736     "X        c #FFFFFF",
737     "aa......................",
738     "aXa.....................",
739     "aXXa....................",
740     "aXXXa...................",
741     "aXXXXa..................",
742     "aXXXXXa.................",
743     "aXXXXXXa................",
744     "aXXXXXXXa...............",
745     "aXXXXXXXXa..............",
746     "aXXXXXXXXXa.............",
747     "aXXXXXXaaaa.............",
748     "aXXXaXXa................",
749     "aXXaaXXa................",
750     "aXa..aXXa...............",
751     "aa...aXXa...............",
752     "a.....aXXa..............",
753     "......aXXa.....XXXX.....",
754     ".......aXXa..XXaaaaXX...",
755     ".......aXXa.XaaaaaaaaX..",
756     "........aa.XaaaXXXXaaaX.",
757     "...........XaaaaX..XaaX.",
758     "..........XaaXaaaX..XaaX",
759     "..........XaaXXaaaX.XaaX",
760     "..........XaaX.XaaaXXaaX",
761     "..........XaaX..XaaaXaaX",
762     "...........XaaX..XaaaaX.",
763     "...........XaaaXXXXaaaX.",
764     "............XaaaaaaaaX..",
765     ".............XXaaaaXX...",
766     "...............XXXX....."};
767 
768     if (m_ignoreDragCursor.isNull()) {
769         HCURSOR cursor = LoadCursor(nullptr, IDC_NO);
770         ICONINFO iconInfo = {0, 0, 0, nullptr, nullptr};
771         GetIconInfo(cursor, &iconInfo);
772         BITMAP bmColor = {0, 0, 0, 0, 0, 0, nullptr};
773 
774         if (iconInfo.hbmColor
775             && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor)
776             && bmColor.bmWidth == bmColor.bmWidthBytes / 4) {
777             const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes;
778             auto *colorBits = new uchar[colorBitsLength];
779             GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits);
780             const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight,
781                                     bmColor.bmWidthBytes, QImage::Format_ARGB32);
782 
783             m_ignoreDragCursor = QPixmap::fromImage(colorImage);
784             delete [] colorBits;
785         } else {
786             m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC);
787         }
788 
789         DeleteObject(iconInfo.hbmMask);
790         DeleteObject(iconInfo.hbmColor);
791         DestroyCursor(cursor);
792     }
793     return m_ignoreDragCursor;
794 }
795 
hCursor(const QCursor & c) const796 HCURSOR QWindowsCursor::hCursor(const QCursor &c) const
797 {
798     const Qt::CursorShape shape = c.shape();
799     if (shape == Qt::BitmapCursor) {
800         const auto pit = m_pixmapCursorCache.constFind(QWindowsPixmapCursorCacheKey(c));
801         if (pit != m_pixmapCursorCache.constEnd())
802             return pit.value()->handle();
803     } else {
804         const auto sit = m_standardCursorCache.constFind(shape);
805         if (sit != m_standardCursorCache.constEnd())
806             return sit.value()->handle();
807     }
808     return HCURSOR(nullptr);
809 }
810 
811 /*!
812     \class QWindowsWindowCursor
813     \brief Per-Window cursor. Contains a QCursor and manages its associated system
814      cursor handle resource.
815 
816     \internal
817     \sa QWindowsCursor
818 */
819 
820 QT_END_NAMESPACE
821 
822 #endif // !QT_NO_CURSOR
823