Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowscursor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QT_NO_CURSOR
5#include "qwindowscursor.h"
6#include "qwindowscontext.h"
7#include "qwindowswindow.h"
8#include "qwindowsscreen.h"
9
10#include <QtGui/qbitmap.h>
11#include <QtGui/qimage.h>
12#include <QtGui/qbitmap.h>
13#include <QtGui/qguiapplication.h>
14#include <QtGui/qscreen.h>
15#include <QtGui/private/qguiapplication_p.h> // getPixmapCursor()
16#include <QtGui/private/qhighdpiscaling_p.h>
17#include <QtGui/private/qpixmap_win_p.h>
18#include <QtCore/private/qwinregistry_p.h>
19
20#include <QtCore/qdebug.h>
21#include <QtCore/qscopedpointer.h>
22
23static bool initResources()
24{
25#if QT_CONFIG(imageformat_png)
26 Q_INIT_RESOURCE(cursors);
27#endif
28 return true;
29}
30
32
33using namespace Qt::Literals::StringLiterals;
34
43 : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0)
44{
45 if (!bitmapCacheKey) {
46 Q_ASSERT(!c.bitmap().isNull());
47 Q_ASSERT(!c.mask().isNull());
48 bitmapCacheKey = c.bitmap().cacheKey();
49 maskCacheKey = c.mask().cacheKey();
50 }
51}
52
67{
68 HCURSOR cur = nullptr;
69 const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatio();
70 if (!qFuzzyCompare(pixmapScaleFactor, 1)) {
71 pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(),
73 }
75 if (mask.isNull()) {
76 mask = QBitmap(pixmap.size());
77 mask.fill(Qt::color1);
78 }
79
80 HBITMAP ic = qt_pixmapToWinHBITMAP(pixmap, /* HBitmapAlpha */ 2);
81 const HBITMAP im = qt_createIconMask(mask);
82
83 ICONINFO ii;
84 ii.fIcon = 0;
85 ii.xHotspot = DWORD(qRound(hotSpot.x() * scaleFactor));
86 ii.yHotspot = DWORD(qRound(hotSpot.y() * scaleFactor));
87 ii.hbmMask = im;
88 ii.hbmColor = ic;
89
90 cur = CreateIconIndirect(&ii);
91
92 DeleteObject(ic);
93 DeleteObject(im);
94 return cur;
95}
96
97// Create a cursor from image and mask of the format QImage::Format_Mono.
98static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits,
99 QPoint hotSpot = QPoint(-1, -1),
100 bool invb = false, bool invm = false)
101{
102 const int width = bbits.width();
103 const int height = bbits.height();
104 if (hotSpot.x() < 0)
105 hotSpot.setX(width / 2);
106 if (hotSpot.y() < 0)
107 hotSpot.setY(height / 2);
108 // a ddb is word aligned, QImage depends on bow it was created
109 const auto bplDdb = qMax(1, ((width + 15) >> 4) << 1);
110 const auto bplImg = int(bbits.bytesPerLine());
111 QScopedArrayPointer<uchar> xBits(new uchar[height * bplDdb]);
112 QScopedArrayPointer<uchar> xMask(new uchar[height * bplDdb]);
113 int x = 0;
114 for (int i = 0; i < height; ++i) {
115 const uchar *bits = bbits.constScanLine(i);
116 const uchar *mask = mbits.constScanLine(i);
117 for (int j = 0; j < bplImg && j < bplDdb; ++j) {
118 uchar b = bits[j];
119 uchar m = mask[j];
120 if (invb)
121 b ^= 0xff;
122 if (invm)
123 m ^= 0xff;
124 xBits[x] = ~m;
125 xMask[x] = b ^ m;
126 ++x;
127 }
128 for (int i = bplImg; i < bplDdb; ++i) {
129 xBits[x] = 0;
130 xMask[x] = 0;
131 ++x;
132 }
133 }
134 return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height,
135 xBits.data(), xMask.data());
136}
137
138// Create a cursor from image and mask of the format QImage::Format_Mono.
139static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1)
140{
142 QImage bbits = cursor.bitmap().toImage();
143 QImage mbits = cursor.mask().toImage();
144 scaleFactor /= bbits.devicePixelRatio();
145 if (!qFuzzyCompare(scaleFactor, 1)) {
146 const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize();
147 bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
148 mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
149 }
150 bbits = std::move(bbits).convertToFormat(QImage::Format_Mono);
151 mbits = std::move(mbits).convertToFormat(QImage::Format_Mono);
152 const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
153 const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
154 return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm);
155}
156
157static QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); }
158
160{
161 const QSize primaryScreenCursorSize = systemCursorSize();
162 if (screen) {
163 // Correct the size if the DPI value of the screen differs from
164 // that of the primary screen.
165 if (const QScreen *primaryQScreen = QGuiApplication::primaryScreen()) {
166 const QPlatformScreen *primaryScreen = primaryQScreen->handle();
167 if (screen != primaryScreen) {
168 const qreal logicalDpi = screen->logicalDpi().first;
169 const qreal primaryScreenLogicalDpi = primaryScreen->logicalDpi().first;
170 if (!qFuzzyCompare(logicalDpi, primaryScreenLogicalDpi))
171 return (QSizeF(primaryScreenCursorSize) * logicalDpi / primaryScreenLogicalDpi).toSize();
172 }
173 }
174 }
175 return primaryScreenCursorSize;
176}
177
178#if !QT_CONFIG(imageformat_png)
179
180static inline QSize standardCursorSize() { return QSize(32, 32); }
181
182// Create pixmap cursors from data and scale the image if the cursor size is
183// higher than the standard 32. Note that bitmap cursors as produced by
184// createBitmapCursor() only work for standard sizes (32,48,64...), which does
185// not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting
186// in a non-standard 24x24 size).
188 // The cursor size the bitmap is targeted for
189 const QSize &bitmapTargetCursorSize,
190 // The actual size of the bitmap data
191 int bitmapSize, const uchar *bits,
192 const uchar *maskBits)
193{
194 QPixmap rawImage = QPixmap::fromImage(QBitmap::fromData(QSize(bitmapSize, bitmapSize), bits).toImage());
195 rawImage.setMask(QBitmap::fromData(QSize(bitmapSize, bitmapSize), maskBits));
196
197 const qreal factor = qreal(screenCursorSize.width()) / qreal(bitmapTargetCursorSize.width());
198 // Scale images if the cursor size is significantly different, starting with 150% where the system cursor
199 // size is 48.
200 if (qAbs(factor - 1.0) > 0.4) {
201 const QTransform transform = QTransform::fromScale(factor, factor);
202 rawImage = rawImage.transformed(transform, Qt::SmoothTransformation);
203 }
204 const QPoint hotSpot(rawImage.width() / 2, rawImage.height() / 2);
205 return QWindowsCursor::PixmapCursor(rawImage, hotSpot);
206}
207
209 const QPlatformScreen *screen)
210{
211 // Non-standard Windows cursors are created from bitmaps
212 static const uchar vsplit_bits[] = {
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
216 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
217 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
219 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
220 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
221 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
224 static const uchar vsplitm_bits[] = {
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
227 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
228 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
229 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
230 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
231 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
232 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
233 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
236 static const uchar hsplit_bits[] = {
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
240 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
241 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
242 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
243 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
244 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
248 static const uchar hsplitm_bits[] = {
249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
252 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
253 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
254 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
255 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
256 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
257 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
260 static const uchar openhand_bits[] = {
261 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
262 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
263 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
264 static const uchar openhandm_bits[] = {
265 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
266 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
267 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
268 static const uchar closedhand_bits[] = {
269 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
270 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
271 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
272 static const uchar closedhandm_bits[] = {
273 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
274 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
275 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
276
277 static const char * const moveDragCursorXpmC[] = {
278 "11 20 3 1",
279 ". c None",
280 "a c #FFFFFF",
281 "X c #000000", // X11 cursor is traditionally black
282 "aa.........",
283 "aXa........",
284 "aXXa.......",
285 "aXXXa......",
286 "aXXXXa.....",
287 "aXXXXXa....",
288 "aXXXXXXa...",
289 "aXXXXXXXa..",
290 "aXXXXXXXXa.",
291 "aXXXXXXXXXa",
292 "aXXXXXXaaaa",
293 "aXXXaXXa...",
294 "aXXaaXXa...",
295 "aXa..aXXa..",
296 "aa...aXXa..",
297 "a.....aXXa.",
298 "......aXXa.",
299 ".......aXXa",
300 ".......aXXa",
301 "........aa."};
302
303 static const char * const copyDragCursorXpmC[] = {
304 "24 30 3 1",
305 ". c None",
306 "a c #000000",
307 "X c #FFFFFF",
308 "XX......................",
309 "XaX.....................",
310 "XaaX....................",
311 "XaaaX...................",
312 "XaaaaX..................",
313 "XaaaaaX.................",
314 "XaaaaaaX................",
315 "XaaaaaaaX...............",
316 "XaaaaaaaaX..............",
317 "XaaaaaaaaaX.............",
318 "XaaaaaaXXXX.............",
319 "XaaaXaaX................",
320 "XaaXXaaX................",
321 "XaX..XaaX...............",
322 "XX...XaaX...............",
323 "X.....XaaX..............",
324 "......XaaX..............",
325 ".......XaaX.............",
326 ".......XaaX.............",
327 "........XX...aaaaaaaaaaa",
328 ".............aXXXXXXXXXa",
329 ".............aXXXXXXXXXa",
330 ".............aXXXXaXXXXa",
331 ".............aXXXXaXXXXa",
332 ".............aXXaaaaaXXa",
333 ".............aXXXXaXXXXa",
334 ".............aXXXXaXXXXa",
335 ".............aXXXXXXXXXa",
336 ".............aXXXXXXXXXa",
337 ".............aaaaaaaaaaa"};
338
339 static const char * const linkDragCursorXpmC[] = {
340 "24 30 3 1",
341 ". c None",
342 "a c #000000",
343 "X c #FFFFFF",
344 "XX......................",
345 "XaX.....................",
346 "XaaX....................",
347 "XaaaX...................",
348 "XaaaaX..................",
349 "XaaaaaX.................",
350 "XaaaaaaX................",
351 "XaaaaaaaX...............",
352 "XaaaaaaaaX..............",
353 "XaaaaaaaaaX.............",
354 "XaaaaaaXXXX.............",
355 "XaaaXaaX................",
356 "XaaXXaaX................",
357 "XaX..XaaX...............",
358 "XX...XaaX...............",
359 "X.....XaaX..............",
360 "......XaaX..............",
361 ".......XaaX.............",
362 ".......XaaX.............",
363 "........XX...aaaaaaaaaaa",
364 ".............aXXXXXXXXXa",
365 ".............aXXXaaaaXXa",
366 ".............aXXXXaaaXXa",
367 ".............aXXXaaaaXXa",
368 ".............aXXaaaXaXXa",
369 ".............aXXaaXXXXXa",
370 ".............aXXaXXXXXXa",
371 ".............aXXXaXXXXXa",
372 ".............aXXXXXXXXXa",
373 ".............aaaaaaaaaaa"};
374
375 switch (cursorShape) {
376 case Qt::SplitVCursor:
378 case Qt::SplitHCursor:
385 return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0));
387 return QWindowsCursor::PixmapCursor(QPixmap(moveDragCursorXpmC), QPoint(0, 0));
389 return QWindowsCursor::PixmapCursor(QPixmap(linkDragCursorXpmC), QPoint(0, 0));
390 }
391
393}
394#else // QT_NO_IMAGEFORMAT_PNG
395struct QWindowsCustomPngCursor {
396 Qt::CursorShape shape;
397 int size;
398 const char *fileName;
399 int hotSpotX;
400 int hotSpotY;
401};
402
404{
405 static const QWindowsCustomPngCursor pngCursors[] = {
406 { Qt::SplitVCursor, 32, "splitvcursor_32.png", 11, 11 },
407 { Qt::SplitVCursor, 48, "splitvcursor_48.png", 16, 17 },
408 { Qt::SplitVCursor, 64, "splitvcursor_64.png", 22, 22 },
409 { Qt::SplitHCursor, 32, "splithcursor_32.png", 11, 11 },
410 { Qt::SplitHCursor, 48, "splithcursor_48.png", 16, 17 },
411 { Qt::SplitHCursor, 64, "splithcursor_64.png", 22, 22 },
412 { Qt::OpenHandCursor, 32, "openhandcursor_32.png", 10, 12 },
413 { Qt::OpenHandCursor, 48, "openhandcursor_48.png", 15, 16 },
414 { Qt::OpenHandCursor, 64, "openhandcursor_64.png", 20, 24 },
415 { Qt::ClosedHandCursor, 32, "closedhandcursor_32.png", 10, 12 },
416 { Qt::ClosedHandCursor, 48, "closedhandcursor_48.png", 15, 16 },
417 { Qt::ClosedHandCursor, 64, "closedhandcursor_64.png", 20, 24 },
418 { Qt::DragCopyCursor, 32, "dragcopycursor_32.png", 0, 0 },
419 { Qt::DragCopyCursor, 48, "dragcopycursor_48.png", 0, 0 },
420 { Qt::DragCopyCursor, 64, "dragcopycursor_64.png", 0, 0 },
421 { Qt::DragMoveCursor, 32, "dragmovecursor_32.png", 0, 0 },
422 { Qt::DragMoveCursor, 48, "dragmovecursor_48.png", 0, 0 },
423 { Qt::DragMoveCursor, 64, "dragmovecursor_64.png", 0, 0 },
424 { Qt::DragLinkCursor, 32, "draglinkcursor_32.png", 0, 0 },
425 { Qt::DragLinkCursor, 48, "draglinkcursor_48.png", 0, 0 },
426 { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 }
427 };
428
429 const QSize cursorSize = screenCursorSize(screen);
430 const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]);
431 const QWindowsCustomPngCursor *bestFit = nullptr;
432 int sizeDelta = INT_MAX;
433 for (const QWindowsCustomPngCursor *s = pngCursors; s < sEnd; ++s) {
434 if (s->shape != cursorShape)
435 continue;
436 const int currentSizeDelta = qMax(s->size, cursorSize.width()) - qMin(s->size, cursorSize.width());
437 if (currentSizeDelta < sizeDelta) {
438 bestFit = s;
439 if (currentSizeDelta == 0)
440 break; // Perfect match found
441 sizeDelta = currentSizeDelta;
442 }
443 }
444
445 if (!bestFit)
446 return PixmapCursor();
447
448 const QPixmap rawImage(":/qt-project.org/windows/cursors/images/"_L1 +
449 QLatin1StringView(bestFit->fileName));
450 return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY));
451}
452#endif // !QT_NO_IMAGEFORMAT_PNG
453
458
460{
461 Q_ASSERT(cursorShape != Qt::BitmapCursor);
462
463 static const QWindowsStandardCursorMapping standardCursors[] = {
464 { Qt::ArrowCursor, IDC_ARROW},
465 { Qt::UpArrowCursor, IDC_UPARROW },
466 { Qt::CrossCursor, IDC_CROSS },
467 { Qt::WaitCursor, IDC_WAIT },
468 { Qt::IBeamCursor, IDC_IBEAM },
469 { Qt::SizeVerCursor, IDC_SIZENS },
470 { Qt::SizeHorCursor, IDC_SIZEWE },
471 { Qt::SizeBDiagCursor, IDC_SIZENESW },
472 { Qt::SizeFDiagCursor, IDC_SIZENWSE },
473 { Qt::SizeAllCursor, IDC_SIZEALL },
474 { Qt::ForbiddenCursor, IDC_NO },
475 { Qt::WhatsThisCursor, IDC_HELP },
476 { Qt::BusyCursor, IDC_APPSTARTING },
478 };
479
480 switch (cursorShape) {
481 case Qt::BlankCursor: {
483 blank.fill(0); // ignore color table
484 return createBitmapCursor(blank, blank);
485 }
486 case Qt::SplitVCursor:
487 case Qt::SplitHCursor:
494 default:
495 break;
496 }
497
498 // Load available standard cursors from resources
499 for (const QWindowsStandardCursorMapping &s : standardCursors) {
500 if (s.shape == cursorShape) {
501 return static_cast<HCURSOR>(LoadImage(nullptr, s.resource, IMAGE_CURSOR,
502 0, 0, LR_DEFAULTSIZE | LR_SHARED));
503 }
504 }
505
506 qWarning("%s: Invalid cursor shape %d", __FUNCTION__, cursorShape);
507 return nullptr;
508}
509
515{
516 StandardCursorCache::Iterator it = m_standardCursorCache.find(shape);
517 if (it == m_standardCursorCache.end()) {
518 if (const HCURSOR hc = QWindowsCursor::createCursorFromShape(shape, m_screen))
519 it = m_standardCursorCache.insert(shape, CursorHandlePtr(new CursorHandle(hc)));
520 }
521 return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle);
522}
523
524HCURSOR QWindowsCursor::m_overriddenCursor = nullptr;
525HCURSOR QWindowsCursor::m_overrideCursor = nullptr;
526
532{
534 PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey);
535 if (it == m_pixmapCursorCache.end()) {
536 if (m_pixmapCursorCache.size() > 50) {
537 // Prevent the cursor cache from growing indefinitely hitting GDI resource
538 // limits if new pixmap cursors are created repetitively by purging out
539 // all-noncurrent pixmap cursors (QTBUG-43515)
540 const HCURSOR currentCursor = GetCursor();
541 for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) {
542 if (it.value()->handle() != currentCursor)
543 it = m_pixmapCursorCache.erase(it);
544 else
545 ++it;
546 }
547 }
548 const qreal scaleFactor = QHighDpiScaling::factor(m_screen);
549 const QPixmap pixmap = c.pixmap();
550 const HCURSOR hc = pixmap.isNull()
551 ? createBitmapCursor(c, scaleFactor)
552 : QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot(), scaleFactor);
553 it = m_pixmapCursorCache.insert(cacheKey, CursorHandlePtr(new CursorHandle(hc)));
554 }
555 return it.value();
556}
557
559 : m_screen(screen)
560{
561 static const bool dummy = initResources();
562 Q_UNUSED(dummy);
563}
564
565inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor)
566{
567 return cursor.shape() == Qt::BitmapCursor
569 : standardWindowCursor(cursor.shape());
570}
571
580{
582 if (!platformWindow) // Desktop/Foreign window.
583 return;
584
585 if (!cursorIn) {
586 platformWindow->setCursor(CursorHandlePtr(new CursorHandle));
587 return;
588 }
589 const CursorHandlePtr wcursor = cursorHandle(*cursorIn);
590 if (wcursor->handle()) {
591 platformWindow->setCursor(wcursor);
592 } else {
593 qWarning("%s: Unable to obtain system cursor for %d",
594 __FUNCTION__, cursorIn->shape());
595 }
596}
597
598// QTBUG-69637: Override cursors can get reset externally when moving across
599// window borders. Enforce the cursor again (to be called from enter event).
601{
602 if (hasOverrideCursor() && m_overrideCursor != GetCursor())
603 SetCursor(m_overrideCursor);
604}
605
607{
608 const CursorHandlePtr wcursor = cursorHandle(cursor);
609 if (const auto overrideCursor = wcursor->handle()) {
610 m_overrideCursor = overrideCursor;
611 const HCURSOR previousCursor = SetCursor(overrideCursor);
612 if (m_overriddenCursor == nullptr)
613 m_overriddenCursor = previousCursor;
614 } else {
615 qWarning("%s: Unable to obtain system cursor for %d",
616 __FUNCTION__, cursor.shape());
617 }
618}
619
621{
622 if (m_overriddenCursor) {
623 SetCursor(m_overriddenCursor);
624 m_overriddenCursor = m_overrideCursor = nullptr;
625 }
626 auto &windows = QWindowsContext::instance()->windows();
627 for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) {
628 if (it.value()->screen() == m_screen)
630 }
631}
632
634{
635 POINT p;
636 GetCursorPos(&p);
637 return QPoint(p.x, p.y);
638}
639
641{
642 enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; // Windows 8: CURSOR_SUPPRESSED
643 CURSORINFO cursorInfo;
644 cursorInfo.cbSize = sizeof(CURSORINFO);
645 if (GetCursorInfo(&cursorInfo)) {
646 if (cursorInfo.flags & cursorShowing)
647 return State::Showing;
648 if (cursorInfo.flags & cursorSuppressed)
649 return State::Suppressed;
650 }
651 return State::Hidden;
652}
653
655{
656 return mousePosition();
657}
658
660{
661 SetCursorPos(pos.x() , pos.y());
662}
663
664/*
665 The standard size is 32x32, even though the cursor is actually just
666 16 pixels large. If a large cursor is set in the accessibility settings,
667 then the cursor increases with 8 pixels for each step.
668*/
670{
671 const QPair<DWORD,bool> cursorSizeSetting =
672 QWinRegistryKey(HKEY_CURRENT_USER, LR"(Control Panel\Cursors)")
673 .dwordValue(L"CursorBaseSize");
674 const int baseSize = screenCursorSize(m_screen).width() / 2;
675 if (!cursorSizeSetting.second)
676 return QSize(baseSize / 2, baseSize / 2);
677
678 // The registry values are dpi-independent, so we need to scale the result.
679 int cursorSizeValue = cursorSizeSetting.first * m_screen->logicalDpi().first
680 / m_screen->logicalBaseDpi().first;
681
682 // map from registry value 32-256 to 0-14, and from there to pixels
683 cursorSizeValue = (cursorSizeValue - 2 * baseSize) / baseSize;
684 const int cursorSize = baseSize + cursorSizeValue * (baseSize / 2);
685 return QSize(cursorSize, cursorSize);
686}
687
689{
690 switch (action) {
691 case Qt::CopyAction:
692 if (m_copyDragCursor.isNull())
693 m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor, m_screen).pixmap;
694 return m_copyDragCursor;
696 case Qt::MoveAction:
697 if (m_moveDragCursor.isNull())
698 m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor, m_screen).pixmap;
699 return m_moveDragCursor;
700 case Qt::LinkAction:
701 if (m_linkDragCursor.isNull())
702 m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor, m_screen).pixmap;
703 return m_linkDragCursor;
704 default:
705 break;
706 }
707
708 static const char * const ignoreDragCursorXpmC[] = {
709 "24 30 3 1",
710 ". c None",
711 "a c #000000",
712 "X c #FFFFFF",
713 "aa......................",
714 "aXa.....................",
715 "aXXa....................",
716 "aXXXa...................",
717 "aXXXXa..................",
718 "aXXXXXa.................",
719 "aXXXXXXa................",
720 "aXXXXXXXa...............",
721 "aXXXXXXXXa..............",
722 "aXXXXXXXXXa.............",
723 "aXXXXXXaaaa.............",
724 "aXXXaXXa................",
725 "aXXaaXXa................",
726 "aXa..aXXa...............",
727 "aa...aXXa...............",
728 "a.....aXXa..............",
729 "......aXXa.....XXXX.....",
730 ".......aXXa..XXaaaaXX...",
731 ".......aXXa.XaaaaaaaaX..",
732 "........aa.XaaaXXXXaaaX.",
733 "...........XaaaaX..XaaX.",
734 "..........XaaXaaaX..XaaX",
735 "..........XaaXXaaaX.XaaX",
736 "..........XaaX.XaaaXXaaX",
737 "..........XaaX..XaaaXaaX",
738 "...........XaaX..XaaaaX.",
739 "...........XaaaXXXXaaaX.",
740 "............XaaaaaaaaX..",
741 ".............XXaaaaXX...",
742 "...............XXXX....."};
743
744 if (m_ignoreDragCursor.isNull()) {
745 HCURSOR cursor = LoadCursor(nullptr, IDC_NO);
746 ICONINFO iconInfo = {0, 0, 0, nullptr, nullptr};
747 GetIconInfo(cursor, &iconInfo);
748 BITMAP bmColor = {0, 0, 0, 0, 0, 0, nullptr};
749
750 if (iconInfo.hbmColor
751 && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor)
752 && bmColor.bmWidth == bmColor.bmWidthBytes / 4) {
753 const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes;
754 auto *colorBits = new uchar[colorBitsLength];
755 GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits);
756 const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight,
757 bmColor.bmWidthBytes, QImage::Format_ARGB32);
758
759 m_ignoreDragCursor = QPixmap::fromImage(colorImage);
760 delete [] colorBits;
761 } else {
762 m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC);
763 }
764
765 DeleteObject(iconInfo.hbmMask);
766 DeleteObject(iconInfo.hbmColor);
767 DestroyCursor(cursor);
768 }
769 return m_ignoreDragCursor;
770}
771
773{
774 const Qt::CursorShape shape = c.shape();
775 if (shape == Qt::BitmapCursor) {
776 const auto pit = m_pixmapCursorCache.constFind(QWindowsPixmapCursorCacheKey(c));
777 if (pit != m_pixmapCursorCache.constEnd())
778 return pit.value()->handle();
779 } else {
780 const auto sit = m_standardCursorCache.constFind(shape);
781 if (sit != m_standardCursorCache.constEnd())
782 return sit.value()->handle();
783 }
784 return HCURSOR(nullptr);
785}
786
797
798#endif // !QT_NO_CURSOR
\inmodule QtGui
Definition qbitmap.h:16
static QBitmap fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat=QImage::Format_MonoLSB)
Constructs a bitmap with the given size, and sets the contents to the bits supplied.
Definition qbitmap.cpp:207
The QCursor class provides a mouse cursor with an arbitrary shape.
Definition qcursor.h:45
QBitmap bitmap() const
Returns the cursor bitmap, or a null bitmap if it is one of the standard cursors.
Definition qcursor.cpp:545
Qt::CursorShape shape() const
Returns the cursor shape identifier.
Definition qcursor.cpp:498
QPoint hotSpot() const
Returns the cursor hot spot, or (0, 0) if it is one of the standard cursors.
Definition qcursor.cpp:595
QBitmap mask() const
Returns the cursor bitmap mask, or a null bitmap if it is one of the standard cursors.
Definition qcursor.cpp:571
QScreen * primaryScreen
the primary (or default) screen of the application.
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
iterator Iterator
Qt-style synonym for QHash::iterator.
Definition qhash.h:1288
iterator erase(const_iterator it)
Definition qhash.h:1233
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
static qreal factor(C *context)
\inmodule QtGui
Definition qimage.h:37
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32
Definition qimage.h:47
qreal devicePixelRatio() const
Returns the device pixel ratio for the image.
Definition qimage.cpp:1482
Native interface to QPlatformWindow. \inmodule QtGui.
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:408
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
QBitmap mask() const
Returns true if this pixmap has an alpha channel, or has a mask, otherwise returns false.
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1437
The QPlatformScreen class provides an abstraction for visual displays.
virtual QDpi logicalDpi() const
Reimplement this function in subclass to return the logical horizontal and vertical dots per inch met...
virtual QDpi logicalBaseDpi() const
Reimplement to return the base logical DPI for the platform.
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
QSize scaled(int w, int h, Qt::AspectRatioMode mode) const noexcept
Definition qsize.h:151
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
static QTransform fromScale(qreal dx, qreal dy)
Creates a matrix which corresponds to a scaling of sx horizontally and sy vertically.
std::pair< DWORD, bool > dwordValue(QStringView subKey) const
\inmodule QtGui
Definition qwindow.h:63
static QWindowsContext * instance()
static bool hasOverrideCursor()
static QPoint mousePosition()
void setOverrideCursor(const QCursor &cursor) override
Reimplement this function in subclass to set an override cursor on the associated screen and return t...
static HCURSOR createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor=1)
void changeCursor(QCursor *widgetCursor, QWindow *widget) override
Set a cursor on a window.
static PixmapCursor customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen=nullptr)
HCURSOR hCursor(const QCursor &c) const
void clearOverrideCursor() override
Reimplement this function in subclass to clear the override cursor.
CursorHandlePtr standardWindowCursor(Qt::CursorShape s=Qt::ArrowCursor)
Return cached standard cursor resources or create new ones.
static State cursorState()
static HCURSOR createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen=nullptr)
static void enforceOverrideCursor()
QWindowsCursor(const QPlatformScreen *screen)
QPixmap dragDefaultCursor(Qt::DropAction action) const
CursorHandlePtr pixmapWindowCursor(const QCursor &c)
Return cached pixmap cursor or create new one.
QSize size() const override
Returns the size of the cursor, in native pixels.
QPoint pos() const override
void setPos(const QPoint &pos) override
static QWindowsWindow * windowsWindowOf(const QWindow *w)
QCursor cursor
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ SmoothTransformation
@ KeepAspectRatio
CursorShape
@ BlankCursor
@ CrossCursor
@ DragCopyCursor
@ BitmapCursor
@ PointingHandCursor
@ SizeHorCursor
@ SizeAllCursor
@ WaitCursor
@ SizeVerCursor
@ DragLinkCursor
@ OpenHandCursor
@ SizeFDiagCursor
@ WhatsThisCursor
@ ArrowCursor
@ SplitVCursor
@ UpArrowCursor
@ ClosedHandCursor
@ DragMoveCursor
@ IBeamCursor
@ SizeBDiagCursor
@ ForbiddenCursor
@ BusyCursor
@ SplitHCursor
@ color1
Definition qnamespace.h:29
DropAction
@ CopyAction
@ MoveAction
@ TargetMoveAction
@ LinkAction
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
static QByteArray cacheKey(Args &&...args)
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLint GLsizei width
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLfloat GLfloat p
[1]
static void initResources()
Definition qpdf.cpp:39
static HBITMAP qt_createIconMask(QImage bm)
HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat)
static const uchar openhandm_bits[]
static const uchar vsplitm_bits[]
static const uchar hsplit_bits[]
static const uchar hsplitm_bits[]
static const uchar openhand_bits[]
static const uchar closedhandm_bits[]
static const uchar vsplit_bits[]
static const uchar closedhand_bits[]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
#define IDC_HAND
Definition qt_windows.h:74
QScreen * screen
[1]
Definition main.cpp:29
#define Q_UNUSED(x)
#define Q_INIT_RESOURCE(name)
Definition qtresource.h:14
unsigned char uchar
Definition qtypes.h:32
double qreal
Definition qtypes.h:187
HICON HCURSOR
static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &screenCursorSize, const QSize &bitmapTargetCursorSize, int bitmapSize, const uchar *bits, const uchar *maskBits)
static bool initResources()
static QSize screenCursorSize(const QPlatformScreen *screen=nullptr)
static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, QPoint hotSpot=QPoint(-1, -1), bool invb=false, bool invm=false)
static QSize systemCursorSize()
static QSize standardCursorSize()
QSharedPointer< CursorHandle > CursorHandlePtr
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]
QWindowsPixmapCursorCacheKey(const QCursor &c)