Qt 6.x
The Qt SDK
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
41 : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0)
42{
43 if (!bitmapCacheKey) {
44 Q_ASSERT(!c.bitmap().isNull());
45 Q_ASSERT(!c.mask().isNull());
46 bitmapCacheKey = c.bitmap().cacheKey();
47 maskCacheKey = c.mask().cacheKey();
48 }
49}
50
65{
66 HCURSOR cur = nullptr;
67 const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatio();
68 if (!qFuzzyCompare(pixmapScaleFactor, 1)) {
69 pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(),
71 }
72 QBitmap mask = pixmap.mask();
73 if (mask.isNull()) {
74 mask = QBitmap(pixmap.size());
75 mask.fill(Qt::color1);
76 }
77
78 HBITMAP ic = qt_pixmapToWinHBITMAP(pixmap, /* HBitmapAlpha */ 2);
79 const HBITMAP im = qt_createIconMask(mask);
80
81 ICONINFO ii;
82 ii.fIcon = 0;
83 ii.xHotspot = DWORD(qRound(hotSpot.x() * scaleFactor));
84 ii.yHotspot = DWORD(qRound(hotSpot.y() * scaleFactor));
85 ii.hbmMask = im;
86 ii.hbmColor = ic;
87
88 cur = CreateIconIndirect(&ii);
89
90 DeleteObject(ic);
91 DeleteObject(im);
92 return cur;
93}
94
95// Create a cursor from image and mask of the format QImage::Format_Mono.
96static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits,
97 QPoint hotSpot = QPoint(-1, -1),
98 bool invb = false, bool invm = false)
99{
100 const int width = bbits.width();
101 const int height = bbits.height();
102 if (hotSpot.x() < 0)
103 hotSpot.setX(width / 2);
104 if (hotSpot.y() < 0)
105 hotSpot.setY(height / 2);
106 const int n = qMax(1, width / 8);
109 int x = 0;
110 for (int i = 0; i < height; ++i) {
111 const uchar *bits = bbits.constScanLine(i);
112 const uchar *mask = mbits.constScanLine(i);
113 for (int j = 0; j < n; ++j) {
114 uchar b = bits[j];
115 uchar m = mask[j];
116 if (invb)
117 b ^= 0xff;
118 if (invm)
119 m ^= 0xff;
120 xBits[x] = ~m;
121 xMask[x] = b ^ m;
122 ++x;
123 }
124 }
125 return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height,
126 xBits.data(), xMask.data());
127}
128
129// Create a cursor from image and mask of the format QImage::Format_Mono.
130static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1)
131{
133 QImage bbits = cursor.bitmap().toImage();
134 QImage mbits = cursor.mask().toImage();
135 scaleFactor /= bbits.devicePixelRatio();
136 if (!qFuzzyCompare(scaleFactor, 1)) {
137 const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize();
138 bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
139 mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
140 }
143 const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
144 const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
145 return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm);
146}
147
148static QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); }
149
151{
152 const QSize primaryScreenCursorSize = systemCursorSize();
153 if (screen) {
154 // Correct the size if the DPI value of the screen differs from
155 // that of the primary screen.
156 if (const QScreen *primaryQScreen = QGuiApplication::primaryScreen()) {
157 const QPlatformScreen *primaryScreen = primaryQScreen->handle();
158 if (screen != primaryScreen) {
159 const qreal logicalDpi = screen->logicalDpi().first;
160 const qreal primaryScreenLogicalDpi = primaryScreen->logicalDpi().first;
161 if (!qFuzzyCompare(logicalDpi, primaryScreenLogicalDpi))
162 return (QSizeF(primaryScreenCursorSize) * logicalDpi / primaryScreenLogicalDpi).toSize();
163 }
164 }
165 }
166 return primaryScreenCursorSize;
167}
168
169#if !QT_CONFIG(imageformat_png)
170
171static inline QSize standardCursorSize() { return QSize(32, 32); }
172
173// Create pixmap cursors from data and scale the image if the cursor size is
174// higher than the standard 32. Note that bitmap cursors as produced by
175// createBitmapCursor() only work for standard sizes (32,48,64...), which does
176// not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting
177// in a non-standard 24x24 size).
179 // The cursor size the bitmap is targeted for
180 const QSize &bitmapTargetCursorSize,
181 // The actual size of the bitmap data
182 int bitmapSize, const uchar *bits,
183 const uchar *maskBits)
184{
185 QPixmap rawImage = QPixmap::fromImage(QBitmap::fromData(QSize(bitmapSize, bitmapSize), bits).toImage());
186 rawImage.setMask(QBitmap::fromData(QSize(bitmapSize, bitmapSize), maskBits));
187
188 const qreal factor = qreal(screenCursorSize.width()) / qreal(bitmapTargetCursorSize.width());
189 // Scale images if the cursor size is significantly different, starting with 150% where the system cursor
190 // size is 48.
191 if (qAbs(factor - 1.0) > 0.4) {
192 const QTransform transform = QTransform::fromScale(factor, factor);
193 rawImage = rawImage.transformed(transform, Qt::SmoothTransformation);
194 }
195 const QPoint hotSpot(rawImage.width() / 2, rawImage.height() / 2);
196 return QWindowsCursor::PixmapCursor(rawImage, hotSpot);
197}
198
200 const QPlatformScreen *screen)
201{
202 // Non-standard Windows cursors are created from bitmaps
203 static const uchar vsplit_bits[] = {
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
207 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
208 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
210 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
211 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
212 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
215 static const uchar vsplitm_bits[] = {
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
218 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
219 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
220 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
221 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
222 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
223 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
224 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
227 static const uchar hsplit_bits[] = {
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
231 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
232 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
233 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
234 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
235 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
239 static const uchar hsplitm_bits[] = {
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
243 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
244 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
245 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
246 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
247 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
251 static const uchar openhand_bits[] = {
252 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
253 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
254 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
255 static const uchar openhandm_bits[] = {
256 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
257 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
258 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
259 static const uchar closedhand_bits[] = {
260 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
261 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
262 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
263 static const uchar closedhandm_bits[] = {
264 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
265 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
266 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
267
268 static const char * const moveDragCursorXpmC[] = {
269 "11 20 3 1",
270 ". c None",
271 "a c #FFFFFF",
272 "X c #000000", // X11 cursor is traditionally black
273 "aa.........",
274 "aXa........",
275 "aXXa.......",
276 "aXXXa......",
277 "aXXXXa.....",
278 "aXXXXXa....",
279 "aXXXXXXa...",
280 "aXXXXXXXa..",
281 "aXXXXXXXXa.",
282 "aXXXXXXXXXa",
283 "aXXXXXXaaaa",
284 "aXXXaXXa...",
285 "aXXaaXXa...",
286 "aXa..aXXa..",
287 "aa...aXXa..",
288 "a.....aXXa.",
289 "......aXXa.",
290 ".......aXXa",
291 ".......aXXa",
292 "........aa."};
293
294 static const char * const copyDragCursorXpmC[] = {
295 "24 30 3 1",
296 ". c None",
297 "a c #000000",
298 "X c #FFFFFF",
299 "XX......................",
300 "XaX.....................",
301 "XaaX....................",
302 "XaaaX...................",
303 "XaaaaX..................",
304 "XaaaaaX.................",
305 "XaaaaaaX................",
306 "XaaaaaaaX...............",
307 "XaaaaaaaaX..............",
308 "XaaaaaaaaaX.............",
309 "XaaaaaaXXXX.............",
310 "XaaaXaaX................",
311 "XaaXXaaX................",
312 "XaX..XaaX...............",
313 "XX...XaaX...............",
314 "X.....XaaX..............",
315 "......XaaX..............",
316 ".......XaaX.............",
317 ".......XaaX.............",
318 "........XX...aaaaaaaaaaa",
319 ".............aXXXXXXXXXa",
320 ".............aXXXXXXXXXa",
321 ".............aXXXXaXXXXa",
322 ".............aXXXXaXXXXa",
323 ".............aXXaaaaaXXa",
324 ".............aXXXXaXXXXa",
325 ".............aXXXXaXXXXa",
326 ".............aXXXXXXXXXa",
327 ".............aXXXXXXXXXa",
328 ".............aaaaaaaaaaa"};
329
330 static const char * const linkDragCursorXpmC[] = {
331 "24 30 3 1",
332 ". c None",
333 "a c #000000",
334 "X c #FFFFFF",
335 "XX......................",
336 "XaX.....................",
337 "XaaX....................",
338 "XaaaX...................",
339 "XaaaaX..................",
340 "XaaaaaX.................",
341 "XaaaaaaX................",
342 "XaaaaaaaX...............",
343 "XaaaaaaaaX..............",
344 "XaaaaaaaaaX.............",
345 "XaaaaaaXXXX.............",
346 "XaaaXaaX................",
347 "XaaXXaaX................",
348 "XaX..XaaX...............",
349 "XX...XaaX...............",
350 "X.....XaaX..............",
351 "......XaaX..............",
352 ".......XaaX.............",
353 ".......XaaX.............",
354 "........XX...aaaaaaaaaaa",
355 ".............aXXXXXXXXXa",
356 ".............aXXXaaaaXXa",
357 ".............aXXXXaaaXXa",
358 ".............aXXXaaaaXXa",
359 ".............aXXaaaXaXXa",
360 ".............aXXaaXXXXXa",
361 ".............aXXaXXXXXXa",
362 ".............aXXXaXXXXXa",
363 ".............aXXXXXXXXXa",
364 ".............aaaaaaaaaaa"};
365
366 switch (cursorShape) {
367 case Qt::SplitVCursor:
369 case Qt::SplitHCursor:
376 return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0));
378 return QWindowsCursor::PixmapCursor(QPixmap(moveDragCursorXpmC), QPoint(0, 0));
380 return QWindowsCursor::PixmapCursor(QPixmap(linkDragCursorXpmC), QPoint(0, 0));
381 }
382
384}
385#else // QT_NO_IMAGEFORMAT_PNG
386struct QWindowsCustomPngCursor {
387 Qt::CursorShape shape;
388 int size;
389 const char *fileName;
390 int hotSpotX;
391 int hotSpotY;
392};
393
395{
396 static const QWindowsCustomPngCursor pngCursors[] = {
397 { Qt::SplitVCursor, 32, "splitvcursor_32.png", 11, 11 },
398 { Qt::SplitVCursor, 48, "splitvcursor_48.png", 16, 17 },
399 { Qt::SplitVCursor, 64, "splitvcursor_64.png", 22, 22 },
400 { Qt::SplitHCursor, 32, "splithcursor_32.png", 11, 11 },
401 { Qt::SplitHCursor, 48, "splithcursor_48.png", 16, 17 },
402 { Qt::SplitHCursor, 64, "splithcursor_64.png", 22, 22 },
403 { Qt::OpenHandCursor, 32, "openhandcursor_32.png", 10, 12 },
404 { Qt::OpenHandCursor, 48, "openhandcursor_48.png", 15, 16 },
405 { Qt::OpenHandCursor, 64, "openhandcursor_64.png", 20, 24 },
406 { Qt::ClosedHandCursor, 32, "closedhandcursor_32.png", 10, 12 },
407 { Qt::ClosedHandCursor, 48, "closedhandcursor_48.png", 15, 16 },
408 { Qt::ClosedHandCursor, 64, "closedhandcursor_64.png", 20, 24 },
409 { Qt::DragCopyCursor, 32, "dragcopycursor_32.png", 0, 0 },
410 { Qt::DragCopyCursor, 48, "dragcopycursor_48.png", 0, 0 },
411 { Qt::DragCopyCursor, 64, "dragcopycursor_64.png", 0, 0 },
412 { Qt::DragMoveCursor, 32, "dragmovecursor_32.png", 0, 0 },
413 { Qt::DragMoveCursor, 48, "dragmovecursor_48.png", 0, 0 },
414 { Qt::DragMoveCursor, 64, "dragmovecursor_64.png", 0, 0 },
415 { Qt::DragLinkCursor, 32, "draglinkcursor_32.png", 0, 0 },
416 { Qt::DragLinkCursor, 48, "draglinkcursor_48.png", 0, 0 },
417 { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 }
418 };
419
420 const QSize cursorSize = screenCursorSize(screen);
421 const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]);
422 const QWindowsCustomPngCursor *bestFit = nullptr;
423 int sizeDelta = INT_MAX;
424 for (const QWindowsCustomPngCursor *s = pngCursors; s < sEnd; ++s) {
425 if (s->shape != cursorShape)
426 continue;
427 const int currentSizeDelta = qMax(s->size, cursorSize.width()) - qMin(s->size, cursorSize.width());
428 if (currentSizeDelta < sizeDelta) {
429 bestFit = s;
430 if (currentSizeDelta == 0)
431 break; // Perfect match found
432 sizeDelta = currentSizeDelta;
433 }
434 }
435
436 if (!bestFit)
437 return PixmapCursor();
438
439 const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") +
440 QString::fromLatin1(bestFit->fileName));
441 return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY));
442}
443#endif // !QT_NO_IMAGEFORMAT_PNG
444
447 LPCWSTR resource;
448};
449
451{
452 Q_ASSERT(cursorShape != Qt::BitmapCursor);
453
454 static const QWindowsStandardCursorMapping standardCursors[] = {
455 { Qt::ArrowCursor, IDC_ARROW},
456 { Qt::UpArrowCursor, IDC_UPARROW },
457 { Qt::CrossCursor, IDC_CROSS },
458 { Qt::WaitCursor, IDC_WAIT },
459 { Qt::IBeamCursor, IDC_IBEAM },
460 { Qt::SizeVerCursor, IDC_SIZENS },
461 { Qt::SizeHorCursor, IDC_SIZEWE },
462 { Qt::SizeBDiagCursor, IDC_SIZENESW },
463 { Qt::SizeFDiagCursor, IDC_SIZENWSE },
464 { Qt::SizeAllCursor, IDC_SIZEALL },
465 { Qt::ForbiddenCursor, IDC_NO },
466 { Qt::WhatsThisCursor, IDC_HELP },
467 { Qt::BusyCursor, IDC_APPSTARTING },
469 };
470
471 switch (cursorShape) {
472 case Qt::BlankCursor: {
474 blank.fill(0); // ignore color table
475 return createBitmapCursor(blank, blank);
476 }
477 case Qt::SplitVCursor:
478 case Qt::SplitHCursor:
485 default:
486 break;
487 }
488
489 // Load available standard cursors from resources
490 for (const QWindowsStandardCursorMapping &s : standardCursors) {
491 if (s.shape == cursorShape) {
492 return static_cast<HCURSOR>(LoadImage(nullptr, s.resource, IMAGE_CURSOR,
493 0, 0, LR_DEFAULTSIZE | LR_SHARED));
494 }
495 }
496
497 qWarning("%s: Invalid cursor shape %d", __FUNCTION__, cursorShape);
498 return nullptr;
499}
500
506{
507 StandardCursorCache::Iterator it = m_standardCursorCache.find(shape);
508 if (it == m_standardCursorCache.end()) {
509 if (const HCURSOR hc = QWindowsCursor::createCursorFromShape(shape, m_screen))
510 it = m_standardCursorCache.insert(shape, CursorHandlePtr(new CursorHandle(hc)));
511 }
512 return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle);
513}
514
515HCURSOR QWindowsCursor::m_overriddenCursor = nullptr;
516HCURSOR QWindowsCursor::m_overrideCursor = nullptr;
517
523{
524 const QWindowsPixmapCursorCacheKey cacheKey(c);
525 PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey);
526 if (it == m_pixmapCursorCache.end()) {
527 if (m_pixmapCursorCache.size() > 50) {
528 // Prevent the cursor cache from growing indefinitely hitting GDI resource
529 // limits if new pixmap cursors are created repetitively by purging out
530 // all-noncurrent pixmap cursors (QTBUG-43515)
531 const HCURSOR currentCursor = GetCursor();
532 for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) {
533 if (it.value()->handle() != currentCursor)
534 it = m_pixmapCursorCache.erase(it);
535 else
536 ++it;
537 }
538 }
539 const qreal scaleFactor = QHighDpiScaling::factor(m_screen);
540 const QPixmap pixmap = c.pixmap();
541 const HCURSOR hc = pixmap.isNull()
542 ? createBitmapCursor(c, scaleFactor)
543 : QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot(), scaleFactor);
544 it = m_pixmapCursorCache.insert(cacheKey, CursorHandlePtr(new CursorHandle(hc)));
545 }
546 return it.value();
547}
548
550 : m_screen(screen)
551{
552 static const bool dummy = initResources();
553 Q_UNUSED(dummy);
554}
555
556inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor)
557{
558 return cursor.shape() == Qt::BitmapCursor
560 : standardWindowCursor(cursor.shape());
561}
562
571{
573 if (!platformWindow) // Desktop/Foreign window.
574 return;
575
576 if (!cursorIn) {
577 platformWindow->setCursor(CursorHandlePtr(new CursorHandle));
578 return;
579 }
580 const CursorHandlePtr wcursor = cursorHandle(*cursorIn);
581 if (wcursor->handle()) {
582 platformWindow->setCursor(wcursor);
583 } else {
584 qWarning("%s: Unable to obtain system cursor for %d",
585 __FUNCTION__, cursorIn->shape());
586 }
587}
588
589// QTBUG-69637: Override cursors can get reset externally when moving across
590// window borders. Enforce the cursor again (to be called from enter event).
592{
593 if (hasOverrideCursor() && m_overrideCursor != GetCursor())
594 SetCursor(m_overrideCursor);
595}
596
598{
599 const CursorHandlePtr wcursor = cursorHandle(cursor);
600 if (const auto overrideCursor = wcursor->handle()) {
601 m_overrideCursor = overrideCursor;
602 const HCURSOR previousCursor = SetCursor(overrideCursor);
603 if (m_overriddenCursor == nullptr)
604 m_overriddenCursor = previousCursor;
605 } else {
606 qWarning("%s: Unable to obtain system cursor for %d",
607 __FUNCTION__, cursor.shape());
608 }
609}
610
612{
613 if (m_overriddenCursor) {
614 SetCursor(m_overriddenCursor);
615 m_overriddenCursor = m_overrideCursor = nullptr;
616 }
617 auto &windows = QWindowsContext::instance()->windows();
618 for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) {
619 if (it.value()->screen() == m_screen)
621 }
622}
623
625{
626 POINT p;
627 GetCursorPos(&p);
628 return QPoint(p.x, p.y);
629}
630
632{
633 enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; // Windows 8: CURSOR_SUPPRESSED
634 CURSORINFO cursorInfo;
635 cursorInfo.cbSize = sizeof(CURSORINFO);
636 if (GetCursorInfo(&cursorInfo)) {
637 if (cursorInfo.flags & cursorShowing)
638 return State::Showing;
639 if (cursorInfo.flags & cursorSuppressed)
640 return State::Suppressed;
641 }
642 return State::Hidden;
643}
644
646{
647 return mousePosition();
648}
649
651{
652 SetCursorPos(pos.x() , pos.y());
653}
654
655/*
656 The standard size is 32x32, even though the cursor is actually just
657 16 pixels large. If a large cursor is set in the accessibility settings,
658 then the cursor increases with 8 pixels for each step.
659*/
661{
662 const QPair<DWORD,bool> cursorSizeSetting =
663 QWinRegistryKey(HKEY_CURRENT_USER, LR"(Control Panel\Cursors)")
664 .dwordValue(L"CursorBaseSize");
665 const int baseSize = screenCursorSize(m_screen).width() / 2;
666 if (!cursorSizeSetting.second)
667 return QSize(baseSize / 2, baseSize / 2);
668
669 // The registry values are dpi-independent, so we need to scale the result.
670 int cursorSizeValue = cursorSizeSetting.first * m_screen->logicalDpi().first
671 / m_screen->logicalBaseDpi().first;
672
673 // map from registry value 32-256 to 0-14, and from there to pixels
674 cursorSizeValue = (cursorSizeValue - 2 * baseSize) / baseSize;
675 const int cursorSize = baseSize + cursorSizeValue * (baseSize / 2);
676 return QSize(cursorSize, cursorSize);
677}
678
680{
681 switch (action) {
682 case Qt::CopyAction:
683 if (m_copyDragCursor.isNull())
684 m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor, m_screen).pixmap;
685 return m_copyDragCursor;
687 case Qt::MoveAction:
688 if (m_moveDragCursor.isNull())
689 m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor, m_screen).pixmap;
690 return m_moveDragCursor;
691 case Qt::LinkAction:
692 if (m_linkDragCursor.isNull())
693 m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor, m_screen).pixmap;
694 return m_linkDragCursor;
695 default:
696 break;
697 }
698
699 static const char * const ignoreDragCursorXpmC[] = {
700 "24 30 3 1",
701 ". c None",
702 "a c #000000",
703 "X c #FFFFFF",
704 "aa......................",
705 "aXa.....................",
706 "aXXa....................",
707 "aXXXa...................",
708 "aXXXXa..................",
709 "aXXXXXa.................",
710 "aXXXXXXa................",
711 "aXXXXXXXa...............",
712 "aXXXXXXXXa..............",
713 "aXXXXXXXXXa.............",
714 "aXXXXXXaaaa.............",
715 "aXXXaXXa................",
716 "aXXaaXXa................",
717 "aXa..aXXa...............",
718 "aa...aXXa...............",
719 "a.....aXXa..............",
720 "......aXXa.....XXXX.....",
721 ".......aXXa..XXaaaaXX...",
722 ".......aXXa.XaaaaaaaaX..",
723 "........aa.XaaaXXXXaaaX.",
724 "...........XaaaaX..XaaX.",
725 "..........XaaXaaaX..XaaX",
726 "..........XaaXXaaaX.XaaX",
727 "..........XaaX.XaaaXXaaX",
728 "..........XaaX..XaaaXaaX",
729 "...........XaaX..XaaaaX.",
730 "...........XaaaXXXXaaaX.",
731 "............XaaaaaaaaX..",
732 ".............XXaaaaXX...",
733 "...............XXXX....."};
734
735 if (m_ignoreDragCursor.isNull()) {
736 HCURSOR cursor = LoadCursor(nullptr, IDC_NO);
737 ICONINFO iconInfo = {0, 0, 0, nullptr, nullptr};
738 GetIconInfo(cursor, &iconInfo);
739 BITMAP bmColor = {0, 0, 0, 0, 0, 0, nullptr};
740
741 if (iconInfo.hbmColor
742 && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor)
743 && bmColor.bmWidth == bmColor.bmWidthBytes / 4) {
744 const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes;
745 auto *colorBits = new uchar[colorBitsLength];
746 GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits);
747 const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight,
748 bmColor.bmWidthBytes, QImage::Format_ARGB32);
749
750 m_ignoreDragCursor = QPixmap::fromImage(colorImage);
751 delete [] colorBits;
752 } else {
753 m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC);
754 }
755
756 DeleteObject(iconInfo.hbmMask);
757 DeleteObject(iconInfo.hbmColor);
758 DestroyCursor(cursor);
759 }
760 return m_ignoreDragCursor;
761}
762
764{
765 const Qt::CursorShape shape = c.shape();
766 if (shape == Qt::BitmapCursor) {
767 const auto pit = m_pixmapCursorCache.constFind(QWindowsPixmapCursorCacheKey(c));
768 if (pit != m_pixmapCursorCache.constEnd())
769 return pit.value()->handle();
770 } else {
771 const auto sit = m_standardCursorCache.constFind(shape);
772 if (sit != m_standardCursorCache.constEnd())
773 return sit.value()->handle();
774 }
775 return HCURSOR(nullptr);
776}
777
788
789#endif // !QT_NO_CURSOR
HCURSOR handle() const
\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:548
Qt::CursorShape shape() const
Returns the cursor shape identifier.
Definition qcursor.cpp:499
QPoint hotSpot() const
Returns the cursor hot spot, or (0, 0) if it is one of the standard cursors.
Definition qcursor.cpp:601
QBitmap mask() const
Returns the cursor bitmap mask, or a null bitmap if it is one of the standard cursors.
Definition qcursor.cpp:575
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:1202
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:925
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
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:1209
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator Iterator
Qt-style synonym for QHash::iterator.
Definition qhash.h:1255
iterator erase(const_iterator it)
Definition qhash.h:1223
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
static qreal factor(C *context)
\inmodule QtGui
Definition qimage.h:37
QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qimage.h:208
QRgb color(int i) const
Returns the color in the color table at index i.
Definition qimage.cpp:1555
QSize size() const
Returns the size of the image, i.e.
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32
Definition qimage.h:47
void fill(uint pixel)
Fills the entire image with the given pixelValue.
Definition qimage.cpp:1738
const uchar * constScanLine(int) const
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1657
qreal devicePixelRatio() const
Returns the device pixel ratio for the image.
Definition qimage.cpp:1460
int colorCount() const
Returns the depth of the image.
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const &
Definition qimage.h:124
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
int height() const
Returns the height of the pixmap.
Definition qpixmap.cpp:484
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:412
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:460
int width() const
Returns the width of the pixmap.
Definition qpixmap.cpp:472
void setMask(const QBitmap &)
Sets a mask bitmap.
Definition qpixmap.cpp:547
QPixmap transformed(const QTransform &, Qt::TransformationMode mode=Qt::FastTransformation) const
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:1445
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:23
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
\inmodule QtCore
T * data() const noexcept
Returns the value of the pointer referenced by this object.
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:207
\inmodule QtCore
Definition qsize.h:25
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
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.
QPair< DWORD, bool > dwordValue(QStringView subKey) const
\inmodule QtGui
Definition qwindow.h:63
HandleBaseWindowHash & windows()
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
Raster or OpenGL Window.
static QWindowsWindow * windowsWindowOf(const QWindow *w)
void setCursor(const CursorHandlePtr &c)
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:28
DropAction
@ CopyAction
@ MoveAction
@ TargetMoveAction
@ LinkAction
std::pair< T1, T2 > QPair
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define qWarning
Definition qlogging.h:162
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
GLfloat n
GLuint GLenum GLenum transform
const GLubyte * c
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
static void initResources()
Definition qpdf.cpp:38
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 QStringLiteral(str)
#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:27
double qreal
Definition qtypes.h:92
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)