1// Copyright (C) 2021 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#include "qcursor.h"
5
6#include <qcoreapplication.h>
7#include <qbitmap.h>
8#include <qimage.h>
9#include <qdatastream.h>
10#include <qvariant.h>
11#include <private/qcursor_p.h>
12#include <qdebug.h>
13
14#include <qpa/qplatformcursor.h>
15#include <private/qguiapplication_p.h>
16#include <private/qhighdpiscaling_p.h>
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \class QCursor
22
23 \brief The QCursor class provides a mouse cursor with an arbitrary
24 shape.
25
26 \inmodule QtGui
27 \ingroup appearance
28 \ingroup shared
29
30
31 This class is mainly used to create mouse cursors that are
32 associated with particular widgets and to get and set the position
33 of the mouse cursor.
34
35 Qt has a number of standard cursor shapes, but you can also make
36 custom cursor shapes based on a QBitmap, a mask and a hotspot.
37
38 To associate a cursor with a widget, use QWidget::setCursor(). To
39 associate a cursor with all widgets (normally for a short period
40 of time), use QGuiApplication::setOverrideCursor().
41
42 To set a cursor shape use QCursor::setShape() or use the QCursor
43 constructor which takes the shape as argument, or you can use one
44 of the predefined cursors defined in the \l Qt::CursorShape enum.
45
46 If you want to create a cursor with your own bitmap, either use
47 the QCursor constructor which takes a bitmap and a mask or the
48 constructor which takes a pixmap as arguments.
49
50 To set or get the position of the mouse cursor use the static
51 methods QCursor::pos() and QCursor::setPos().
52
53 \b{Note:} It is possible to create a QCursor before
54 QGuiApplication, but it is not useful except as a place-holder for a
55 real QCursor created after QGuiApplication. Attempting to use a
56 QCursor that was created before QGuiApplication will result in a
57 crash.
58
59 \section1 A Note for X11 Users
60
61 On X11, Qt supports the \l{Xcursor}{Xcursor}
62 library, which allows for full color icon themes. The table below
63 shows the cursor name used for each Qt::CursorShape value. If a
64 cursor cannot be found using the name shown below, a standard X11
65 cursor will be used instead. Note: X11 does not provide
66 appropriate cursors for all possible Qt::CursorShape values. It
67 is possible that some cursors will be taken from the Xcursor
68 theme, while others will use an internal bitmap cursor.
69
70 \table
71 \header \li Shape \li Qt::CursorShape Value \li Cursor Name
72 \li Shape \li Qt::CursorShape Value \li Cursor Name
73 \row \li \inlineimage cursor-arrow.png
74 \li Qt::ArrowCursor \li \c left_ptr
75 \li \inlineimage cursor-sizev.png
76 \li Qt::SizeVerCursor \li \c size_ver
77 \row \li \inlineimage cursor-uparrow.png
78 \li Qt::UpArrowCursor \li \c up_arrow
79 \li \inlineimage cursor-sizeh.png
80 \li Qt::SizeHorCursor \li \c size_hor
81 \row \li \inlineimage cursor-cross.png
82 \li Qt::CrossCursor \li \c cross
83 \li \inlineimage cursor-sizeb.png
84 \li Qt::SizeBDiagCursor \li \c size_bdiag
85 \row \li \inlineimage cursor-ibeam.png
86 \li Qt::IBeamCursor \li \c ibeam
87 \li \inlineimage cursor-sizef.png
88 \li Qt::SizeFDiagCursor \li \c size_fdiag
89 \row \li \inlineimage cursor-wait.png
90 \li Qt::WaitCursor \li \c wait
91 \li \inlineimage cursor-sizeall.png
92 \li Qt::SizeAllCursor \li \c size_all
93 \row \li \inlineimage cursor-busy.png
94 \li Qt::BusyCursor \li \c left_ptr_watch
95 \li \inlineimage cursor-vsplit.png
96 \li Qt::SplitVCursor \li \c split_v
97 \row \li \inlineimage cursor-forbidden.png
98 \li Qt::ForbiddenCursor \li \c forbidden
99 \li \inlineimage cursor-hsplit.png
100 \li Qt::SplitHCursor \li \c split_h
101 \row \li \inlineimage cursor-hand.png
102 \li Qt::PointingHandCursor \li \c pointing_hand
103 \li \inlineimage cursor-openhand.png
104 \li Qt::OpenHandCursor \li \c openhand
105 \row \li \inlineimage cursor-whatsthis.png
106 \li Qt::WhatsThisCursor \li \c whats_this
107 \li \inlineimage cursor-closedhand.png
108 \li Qt::ClosedHandCursor \li \c closedhand
109 \row \li
110 \li Qt::DragMoveCursor \li \c dnd-move or \c move
111 \li
112 \li Qt::DragCopyCursor \li \c dnd-copy or \c copy
113 \row \li
114 \li Qt::DragLinkCursor \li \c dnd-link or \c link
115 \endtable
116
117 \sa QWidget
118*/
119
120/*!
121 \fn QCursor::QCursor(QCursor &&other)
122 \since 5.5
123
124 Move-constructs a cursor from \a other. After being moved from,
125 the only valid operations on \a other are destruction and
126 (move and copy) assignment. The effects of calling any other
127 member function on a moved-from instance are undefined.
128*/
129
130/*!
131 \fn QCursor &QCursor::operator=(QCursor &&other)
132
133 Move-assigns \a other to this QCursor instance.
134
135 \since 5.2
136*/
137
138/*!
139 \fn void QCursor::swap(QCursor &other)
140
141 Swaps this cursor with the \a other cursor.
142
143 \since 5.7
144 */
145
146/*!
147 \fn QPoint QCursor::pos(const QScreen *screen)
148
149 Returns the position of the cursor (hot spot) of the \a screen
150 in global screen coordinates.
151
152 You can call QWidget::mapFromGlobal() to translate it to widget
153 coordinates.
154
155 \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
156*/
157QPoint QCursor::pos(const QScreen *screen)
158{
159 if (screen) {
160 if (const QPlatformCursor *cursor = screen->handle()->cursor()) {
161 const QPlatformScreen *ps = screen->handle();
162 QPoint nativePos = cursor->pos();
163 ps = ps->screenForPosition(point: nativePos);
164 return QHighDpi::fromNativePixels(value: nativePos, context: ps->screen());
165 }
166 }
167 return QGuiApplicationPrivate::lastCursorPosition.toPoint();
168}
169
170/*!
171 \fn QPoint QCursor::pos()
172
173 Returns the position of the cursor (hot spot) of
174 the primary screen in global screen coordinates.
175
176 You can call QWidget::mapFromGlobal() to translate it to widget
177 coordinates.
178
179 \note The position is queried from the windowing system. If mouse events are generated
180 via other means (e.g., via QWindowSystemInterface in a unit test), those fake mouse
181 moves will not be reflected in the returned value.
182
183 \note On platforms where there is no windowing system or cursors are not available, the returned
184 position is based on the mouse move events generated via QWindowSystemInterface.
185
186 \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen()
187*/
188QPoint QCursor::pos()
189{
190 return QCursor::pos(screen: QGuiApplication::primaryScreen());
191}
192
193/*!
194 \fn void QCursor::setPos(QScreen *screen, int x, int y)
195
196 Moves the cursor (hot spot) of the \a screen to the global
197 screen position (\a x, \a y).
198
199 You can call QWidget::mapToGlobal() to translate widget
200 coordinates to global screen coordinates.
201
202 \note Calling this function results in changing the cursor position through the windowing
203 system. The windowing system will typically respond by sending mouse events to the application's
204 window. This means that the usage of this function should be avoided in unit tests and
205 everywhere where fake mouse events are being injected via QWindowSystemInterface because the
206 windowing system's mouse state (with regards to buttons for example) may not match the state in
207 the application-generated events.
208
209 \note On platforms where there is no windowing system or cursors are not available, this
210 function may do nothing.
211
212 \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
213*/
214void QCursor::setPos(QScreen *screen, int x, int y)
215{
216 if (screen) {
217 if (QPlatformCursor *cursor = screen->handle()->cursor()) {
218 const QPoint pos(x, y);
219 const QPoint devicePos = QHighDpi::toNativePixels(value: pos, context: screen->virtualSiblingAt(point: pos));
220 // Need to check, since some X servers generate null mouse move
221 // events, causing looping in applications which call setPos() on
222 // every mouse move event.
223 if (devicePos != cursor->pos())
224 cursor->setPos(devicePos);
225 }
226 }
227}
228
229/*!
230 \fn void QCursor::setPos(int x, int y)
231
232 Moves the cursor (hot spot) of the primary screen
233 to the global screen position (\a x, \a y).
234
235 You can call QWidget::mapToGlobal() to translate widget
236 coordinates to global screen coordinates.
237
238 \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen()
239*/
240void QCursor::setPos(int x, int y)
241{
242 QCursor::setPos(screen: QGuiApplication::primaryScreen(), x, y);
243}
244
245#ifndef QT_NO_CURSOR
246
247/*!
248 \fn void QCursor::setPos (const QPoint &p)
249
250 \overload
251
252 Moves the cursor (hot spot) to the global screen position at point
253 \a p.
254*/
255
256/*!
257 \fn void QCursor::setPos (QScreen *screen,const QPoint &p)
258
259 \overload
260
261 Moves the cursor (hot spot) to the global screen position of the
262 \a screen at point \a p.
263*/
264
265/*****************************************************************************
266 QCursor stream functions
267 *****************************************************************************/
268
269#ifndef QT_NO_DATASTREAM
270
271
272/*!
273 \fn QDataStream &operator<<(QDataStream &stream, const QCursor &cursor)
274 \relates QCursor
275
276 Writes the \a cursor to the \a stream.
277
278 \sa {Serializing Qt Data Types}
279*/
280
281QDataStream &operator<<(QDataStream &s, const QCursor &c)
282{
283 s << (qint16)c.shape(); // write shape id to stream
284 if (c.shape() == Qt::BitmapCursor) { // bitmap cursor
285 bool isPixmap = false;
286 if (s.version() >= 7) {
287 isPixmap = !c.pixmap().isNull();
288 s << isPixmap;
289 }
290 if (isPixmap)
291 s << c.pixmap();
292 else
293 s << c.bitmap() << c.mask();
294 s << c.hotSpot();
295 }
296 return s;
297}
298
299/*!
300 \fn QDataStream &operator>>(QDataStream &stream, QCursor &cursor)
301 \relates QCursor
302
303 Reads the \a cursor from the \a stream.
304
305 \sa {Serializing Qt Data Types}
306*/
307
308QDataStream &operator>>(QDataStream &s, QCursor &c)
309{
310 qint16 shape;
311 s >> shape; // read shape id from stream
312 if (shape == Qt::BitmapCursor) { // read bitmap cursor
313 bool isPixmap = false;
314 if (s.version() >= 7)
315 s >> isPixmap;
316 if (isPixmap) {
317 QPixmap pm;
318 QPoint hot;
319 s >> pm >> hot;
320 c = QCursor(pm, hot.x(), hot.y());
321 } else {
322 QBitmap bm, bmm;
323 QPoint hot;
324 s >> bm >> bmm >> hot;
325 c = QCursor(bm, bmm, hot.x(), hot.y());
326 }
327 } else {
328 c.setShape((Qt::CursorShape)shape); // create cursor with shape
329 }
330 return s;
331}
332#endif // QT_NO_DATASTREAM
333
334
335/*!
336 Constructs a custom pixmap cursor.
337
338 \a pixmap is the image. It is usual to give it a mask (set using
339 QPixmap::setMask()). \a hotX and \a hotY define the cursor's hot
340 spot.
341
342 If \a hotX is negative, it is set to the \c{pixmap().width()/2}.
343 If \a hotY is negative, it is set to the \c{pixmap().height()/2}.
344
345 Valid cursor sizes depend on the display hardware (or the
346 underlying window system). We recommend using 32 x 32 cursors,
347 because this size is supported on all platforms. Some platforms
348 also support 16 x 16, 48 x 48, and 64 x 64 cursors.
349
350 \sa QPixmap::QPixmap(), QPixmap::setMask()
351*/
352
353QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY)
354 : d(nullptr)
355{
356 QImage img = pixmap.toImage().convertToFormat(f: QImage::Format_Indexed8, flags: Qt::ThresholdDither|Qt::AvoidDither);
357 QBitmap bm = QBitmap::fromImage(image: img, flags: Qt::ThresholdDither|Qt::AvoidDither);
358 QBitmap bmm = pixmap.mask();
359 if (!bmm.isNull()) {
360 QBitmap nullBm;
361 bm.setMask(nullBm);
362 }
363 else if (!pixmap.mask().isNull()) {
364 QImage mimg = pixmap.mask().toImage().convertToFormat(f: QImage::Format_Indexed8, flags: Qt::ThresholdDither|Qt::AvoidDither);
365 bmm = QBitmap::fromImage(image: mimg, flags: Qt::ThresholdDither|Qt::AvoidDither);
366 }
367 else {
368 bmm = QBitmap(bm.size());
369 bmm.fill(fillColor: Qt::color1);
370 }
371
372 d = QCursorData::setBitmap(bitmap: bm, mask: bmm, hotX, hotY, devicePixelRatio: pixmap.devicePixelRatio());
373 d->pixmap = pixmap;
374}
375
376
377
378/*!
379 Constructs a custom bitmap cursor.
380
381 \a bitmap and
382 \a mask make up the bitmap.
383 \a hotX and
384 \a hotY define the cursor's hot spot.
385
386 If \a hotX is negative, it is set to the \c{bitmap().width()/2}.
387 If \a hotY is negative, it is set to the \c{bitmap().height()/2}.
388
389 The cursor \a bitmap (B) and \a mask (M) bits are combined like this:
390 \list
391 \li B=1 and M=1 gives black.
392 \li B=0 and M=1 gives white.
393 \li B=0 and M=0 gives transparent.
394 \li B=1 and M=0 gives an XOR'd result under Windows, undefined
395 results on all other platforms.
396 \endlist
397
398 Use the global Qt color Qt::color0 to draw 0-pixels and Qt::color1 to
399 draw 1-pixels in the bitmaps.
400
401 Valid cursor sizes depend on the display hardware (or the
402 underlying window system). We recommend using 32 x 32 cursors,
403 because this size is supported on all platforms. Some platforms
404 also support 16 x 16, 48 x 48, and 64 x 64 cursors.
405
406 \sa QBitmap::QBitmap(), QBitmap::setMask()
407*/
408
409QCursor::QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
410 : d(nullptr)
411{
412 d = QCursorData::setBitmap(bitmap, mask, hotX, hotY, devicePixelRatio: 1.0);
413}
414
415/*!
416 Constructs a cursor with the default arrow shape.
417*/
418QCursor::QCursor()
419{
420 if (!QCursorData::initialized) {
421 if (QCoreApplication::startingUp()) {
422 d = nullptr;
423 return;
424 }
425 QCursorData::initialize();
426 }
427 QCursorData *c = qt_cursorTable[0];
428 c->ref.ref();
429 d = c;
430}
431
432/*!
433 Constructs a cursor with the specified \a shape.
434
435 See \l Qt::CursorShape for a list of shapes.
436
437 \sa setShape()
438*/
439QCursor::QCursor(Qt::CursorShape shape)
440 : d(nullptr)
441{
442 QCursorData::initialize();
443 setShape(shape);
444}
445
446/*!
447 \fn bool QCursor::operator==(const QCursor &lhs, const QCursor &rhs)
448 \since 5.10
449
450 Equality operator. Returns \c true if \a lhs and \a rhs
451 have the same \l{QCursor::}{shape()} and, in the case of
452 \l{Qt::BitmapCursor}{bitmap cursors}, the same \l{QCursor::}{hotSpot()}
453 and either the same \l{QCursor::}{pixmap()} or the same
454 \l{QCursor::}{bitmap()} and \l{QCursor::}{mask()}.
455
456 \note When comparing bitmap cursors, this function only
457 compares the bitmaps' \l{QPixmap::cacheKey()}{cache keys},
458 not each pixel.
459
460 \sa operator!=(const QCursor &lhs, const QCursor &rhs)
461*/
462bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept
463{
464 if (lhs.d == rhs.d)
465 return true; // Copy or same shape
466
467 // Check pixmaps or bitmaps cache keys. Notice that having BitmapCursor
468 // shape implies either non-null pixmap or non-null bitmap and mask
469 if (lhs.shape() == Qt::BitmapCursor && rhs.shape() == Qt::BitmapCursor
470 && lhs.hotSpot() == rhs.hotSpot()) {
471 if (!lhs.d->pixmap.isNull())
472 return lhs.d->pixmap.cacheKey() == rhs.d->pixmap.cacheKey();
473
474 if (!rhs.d->pixmap.isNull())
475 return false;
476
477 return lhs.d->bm->cacheKey() == rhs.d->bm->cacheKey()
478 && lhs.d->bmm->cacheKey() == rhs.d->bmm->cacheKey();
479 }
480
481 return false;
482}
483
484/*!
485 \fn bool QCursor::operator!=(const QCursor &lhs, const QCursor &rhs)
486 \since 5.10
487
488 Inequality operator. Returns the equivalent of !(\a lhs == \a rhs).
489
490 \sa operator==(const QCursor &lhs, const QCursor &rhs)
491*/
492
493/*!
494 Returns the cursor shape identifier.
495
496 \sa setShape()
497*/
498Qt::CursorShape QCursor::shape() const
499{
500 QCursorData::initialize();
501 return d->cshape;
502}
503
504/*!
505 Sets the cursor to the shape identified by \a shape.
506
507 See \l Qt::CursorShape for the list of cursor shapes.
508
509 \sa shape()
510*/
511void QCursor::setShape(Qt::CursorShape shape)
512{
513 QCursorData::initialize();
514 QCursorData *c = uint(shape) <= Qt::LastCursor ? qt_cursorTable[shape] : nullptr;
515 if (!c)
516 c = qt_cursorTable[0];
517 c->ref.ref();
518 if (!d) {
519 d = c;
520 } else {
521 if (!d->ref.deref())
522 delete d;
523 d = c;
524 }
525}
526
527/*!
528 \fn QBitmap QCursor::bitmap(Qt::ReturnByValueConstant) const
529 \since 5.15
530 \deprecated Use the overload without argument instead.
531
532 Returns the cursor bitmap, or a null bitmap if it is one of the
533 standard cursors.
534
535 Previously, Qt provided a version of \c bitmap() which returned the bitmap
536 by-pointer. That version is now removed. To maintain compatibility
537 with old code, this function was provided to differentiate between the by-pointer
538 function and the by-value function.
539*/
540
541/*!
542 Returns the cursor bitmap, or a null bitmap if it is one of the
543 standard cursors.
544*/
545QBitmap QCursor::bitmap() const
546{
547 QCursorData::initialize();
548 if (d->bm)
549 return *(d->bm);
550 return QBitmap();
551}
552
553/*!
554 \fn QBitmap QCursor::mask(Qt::ReturnByValueConstant) const
555 \since 5.15
556 \deprecated Use the overload without argument instead.
557
558 Returns the cursor bitmap mask, or a null bitmap if it is one of the
559 standard cursors.
560
561 Previously, Qt provided a version of \c mask() which returned the bitmap
562 by-pointer. That version is now removed. To maintain compatibility
563 with old code, this function was provided to differentiate between the by-pointer
564 function and the by-value function.
565*/
566
567/*!
568 Returns the cursor bitmap mask, or a null bitmap if it is one of the
569 standard cursors.
570*/
571QBitmap QCursor::mask() const
572{
573 QCursorData::initialize();
574 if (d->bmm)
575 return *(d->bmm);
576 return QBitmap();
577}
578
579/*!
580 Returns the cursor pixmap. This is only valid if the cursor is a
581 pixmap cursor.
582*/
583
584QPixmap QCursor::pixmap() const
585{
586 QCursorData::initialize();
587 return d->pixmap;
588}
589
590/*!
591 Returns the cursor hot spot, or (0, 0) if it is one of the
592 standard cursors.
593*/
594
595QPoint QCursor::hotSpot() const
596{
597 QCursorData::initialize();
598 return QPoint(d->hx, d->hy);
599}
600
601/*!
602 Constructs a copy of the cursor \a c.
603*/
604
605QCursor::QCursor(const QCursor &c)
606{
607 QCursorData::initialize();
608 d = c.d;
609 d->ref.ref();
610}
611
612/*!
613 Destroys the cursor.
614*/
615
616QCursor::~QCursor()
617{
618 if (d && !d->ref.deref())
619 delete d;
620}
621
622
623/*!
624 Assigns \a c to this cursor and returns a reference to this
625 cursor.
626*/
627
628QCursor &QCursor::operator=(const QCursor &c)
629{
630 QCursorData::initialize();
631 if (c.d)
632 c.d->ref.ref();
633 if (d && !d->ref.deref())
634 delete d;
635 d = c.d;
636 return *this;
637}
638
639/*!
640 Returns the cursor as a QVariant.
641*/
642QCursor::operator QVariant() const
643{
644 return QVariant::fromValue(value: *this);
645}
646
647#ifndef QT_NO_DEBUG_STREAM
648QDebug operator<<(QDebug dbg, const QCursor &c)
649{
650 QDebugStateSaver saver(dbg);
651 dbg.nospace() << "QCursor(Qt::CursorShape(" << c.shape() << "))";
652 return dbg;
653}
654#endif
655
656/*****************************************************************************
657 Internal QCursorData class
658 *****************************************************************************/
659
660QCursorData *qt_cursorTable[Qt::LastCursor + 1];
661bool QCursorData::initialized = false;
662
663QCursorData::QCursorData(Qt::CursorShape s)
664 : ref(1), cshape(s), bm(nullptr), bmm(nullptr), hx(0), hy(0)
665{
666}
667
668QCursorData::~QCursorData()
669{
670 delete bm;
671 delete bmm;
672}
673
674/*! \internal */
675void QCursorData::cleanup()
676{
677 if (!QCursorData::initialized)
678 return;
679
680 for (int shape = 0; shape <= Qt::LastCursor; ++shape) {
681 // In case someone has a static QCursor defined with this shape
682 if (!qt_cursorTable[shape]->ref.deref())
683 delete qt_cursorTable[shape];
684 qt_cursorTable[shape] = nullptr;
685 }
686 QCursorData::initialized = false;
687}
688
689/*! \internal */
690void QCursorData::initialize()
691{
692 if (QCursorData::initialized)
693 return;
694 for (int shape = 0; shape <= Qt::LastCursor; ++shape)
695 qt_cursorTable[shape] = new QCursorData((Qt::CursorShape)shape);
696 QCursorData::initialized = true;
697}
698
699QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY, qreal devicePixelRatio)
700{
701 QCursorData::initialize();
702 if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
703 qWarning(msg: "QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
704 QCursorData *c = qt_cursorTable[0];
705 c->ref.ref();
706 return c;
707 }
708 QCursorData *d = new QCursorData;
709 d->bm = new QBitmap(bitmap);
710 d->bmm = new QBitmap(mask);
711 d->cshape = Qt::BitmapCursor;
712 d->hx = hotX >= 0 ? hotX : bitmap.width() / 2 / devicePixelRatio;
713 d->hy = hotY >= 0 ? hotY : bitmap.height() / 2 / devicePixelRatio;
714
715 return d;
716}
717
718void QCursorData::update()
719{
720}
721
722QT_END_NAMESPACE
723#endif // QT_NO_CURSOR
724
725

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/gui/kernel/qcursor.cpp