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