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 \memberswap{cursor}
141
142 \since 5.7
143 */
144
145/*!
146 \fn QPoint QCursor::pos(const QScreen *screen)
147
148 Returns the position of the cursor (hot spot) of the \a screen
149 in global screen coordinates.
150
151 You can call QWidget::mapFromGlobal() to translate it to widget
152 coordinates.
153
154 \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
155*/
156QPoint QCursor::pos(const QScreen *screen)
157{
158 if (screen) {
159 if (const QPlatformCursor *cursor = screen->handle()->cursor()) {
160 const QPlatformScreen *ps = screen->handle();
161 QPoint nativePos = cursor->pos();
162 ps = ps->screenForPosition(point: nativePos);
163 return QHighDpi::fromNativePixels(value: nativePos, context: ps->screen());
164 }
165 }
166 return QGuiApplicationPrivate::lastCursorPosition.toPoint();
167}
168
169/*!
170 \fn QPoint QCursor::pos()
171
172 Returns the position of the cursor (hot spot) of
173 the primary screen in global screen coordinates.
174
175 You can call QWidget::mapFromGlobal() to translate it to widget
176 coordinates.
177
178 \note The position is queried from the windowing system. If mouse events are generated
179 via other means (e.g., via QWindowSystemInterface in a unit test), those fake mouse
180 moves will not be reflected in the returned value.
181
182 \note On platforms where there is no windowing system or cursors are not available, the returned
183 position is based on the mouse move events generated via QWindowSystemInterface.
184
185 \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen()
186*/
187QPoint QCursor::pos()
188{
189 return QCursor::pos(screen: QGuiApplication::primaryScreen());
190}
191
192/*!
193 \fn void QCursor::setPos(QScreen *screen, int x, int y)
194
195 Moves the cursor (hot spot) of the \a screen to the global
196 screen position (\a x, \a y).
197
198 You can call QWidget::mapToGlobal() to translate widget
199 coordinates to global screen coordinates.
200
201 \note Calling this function results in changing the cursor position through the windowing
202 system. The windowing system will typically respond by sending mouse events to the application's
203 window. This means that the usage of this function should be avoided in unit tests and
204 everywhere where fake mouse events are being injected via QWindowSystemInterface because the
205 windowing system's mouse state (with regards to buttons for example) may not match the state in
206 the application-generated events.
207
208 \note On platforms where there is no windowing system or cursors are not available, this
209 function may do nothing.
210
211 \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
212*/
213void QCursor::setPos(QScreen *screen, int x, int y)
214{
215 if (screen) {
216 if (QPlatformCursor *cursor = screen->handle()->cursor()) {
217 const QPoint pos(x, y);
218 const QPoint devicePos = QHighDpi::toNativePixels(value: pos, context: screen->virtualSiblingAt(point: pos));
219 // Need to check, since some X servers generate null mouse move
220 // events, causing looping in applications which call setPos() on
221 // every mouse move event.
222 if (devicePos != cursor->pos())
223 cursor->setPos(devicePos);
224 }
225 }
226}
227
228/*!
229 \fn void QCursor::setPos(int x, int y)
230
231 Moves the cursor (hot spot) of the primary screen
232 to the global screen position (\a x, \a y).
233
234 You can call QWidget::mapToGlobal() to translate widget
235 coordinates to global screen coordinates.
236
237 \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen()
238*/
239void QCursor::setPos(int x, int y)
240{
241 QCursor::setPos(screen: QGuiApplication::primaryScreen(), x, y);
242}
243
244#ifndef QT_NO_CURSOR
245
246/*!
247 \fn void QCursor::setPos (const QPoint &p)
248
249 \overload
250
251 Moves the cursor (hot spot) to the global screen position at point
252 \a p.
253*/
254
255/*!
256 \fn void QCursor::setPos (QScreen *screen,const QPoint &p)
257
258 \overload
259
260 Moves the cursor (hot spot) to the global screen position of the
261 \a screen at point \a p.
262*/
263
264/*****************************************************************************
265 QCursor stream functions
266 *****************************************************************************/
267
268#ifndef QT_NO_DATASTREAM
269
270
271/*!
272 \fn QDataStream &operator<<(QDataStream &stream, const QCursor &cursor)
273 \relates QCursor
274
275 Writes the \a cursor to the \a stream.
276
277 \sa {Serializing Qt Data Types}
278*/
279
280QDataStream &operator<<(QDataStream &s, const QCursor &c)
281{
282 s << (qint16)c.shape(); // write shape id to stream
283 if (c.shape() == Qt::BitmapCursor) { // bitmap cursor
284 bool isPixmap = false;
285 if (s.version() >= 7) {
286 isPixmap = !c.pixmap().isNull();
287 s << isPixmap;
288 }
289 if (isPixmap)
290 s << c.pixmap();
291 else
292 s << c.bitmap() << c.mask();
293 s << c.hotSpot();
294 }
295 return s;
296}
297
298/*!
299 \fn QDataStream &operator>>(QDataStream &stream, QCursor &cursor)
300 \relates QCursor
301
302 Reads the \a cursor from the \a stream.
303
304 \sa {Serializing Qt Data Types}
305*/
306
307QDataStream &operator>>(QDataStream &s, QCursor &c)
308{
309 qint16 shape;
310 s >> shape; // read shape id from stream
311 if (shape == Qt::BitmapCursor) { // read bitmap cursor
312 bool isPixmap = false;
313 if (s.version() >= 7)
314 s >> isPixmap;
315 if (isPixmap) {
316 QPixmap pm;
317 QPoint hot;
318 s >> pm >> hot;
319 c = QCursor(pm, hot.x(), hot.y());
320 } else {
321 QBitmap bm, bmm;
322 QPoint hot;
323 s >> bm >> bmm >> hot;
324 c = QCursor(bm, bmm, hot.x(), hot.y());
325 }
326 } else {
327 c.setShape((Qt::CursorShape)shape); // create cursor with shape
328 }
329 return s;
330}
331#endif // QT_NO_DATASTREAM
332
333
334/*!
335 Constructs a custom pixmap cursor.
336
337 \a pixmap is the image. It is usual to give it a mask (set using
338 QPixmap::setMask()). \a hotX and \a hotY define the cursor's hot
339 spot.
340
341 If \a hotX is negative, it is set to the \c{pixmap().width()/2}.
342 If \a hotY is negative, it is set to the \c{pixmap().height()/2}.
343
344 Valid cursor sizes depend on the display hardware (or the
345 underlying window system). We recommend using 32 x 32 cursors,
346 because this size is supported on all platforms. Some platforms
347 also support 16 x 16, 48 x 48, and 64 x 64 cursors.
348
349 \sa QPixmap::QPixmap(), QPixmap::setMask()
350*/
351
352QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY)
353 : d(nullptr)
354{
355 QImage img = pixmap.toImage().convertToFormat(f: QImage::Format_Indexed8, flags: Qt::ThresholdDither|Qt::AvoidDither);
356 QBitmap bm = QBitmap::fromImage(image: img, flags: Qt::ThresholdDither|Qt::AvoidDither);
357 QBitmap bmm = pixmap.mask();
358 if (!bmm.isNull()) {
359 QBitmap nullBm;
360 bm.setMask(nullBm);
361 }
362 else if (!pixmap.mask().isNull()) {
363 QImage mimg = pixmap.mask().toImage().convertToFormat(f: QImage::Format_Indexed8, flags: Qt::ThresholdDither|Qt::AvoidDither);
364 bmm = QBitmap::fromImage(image: mimg, flags: Qt::ThresholdDither|Qt::AvoidDither);
365 }
366 else {
367 bmm = QBitmap(bm.size());
368 bmm.fill(fillColor: Qt::color1);
369 }
370
371 d = QCursorData::setBitmap(bitmap: bm, mask: bmm, hotX, hotY, devicePixelRatio: pixmap.devicePixelRatio());
372 d->pixmap = pixmap;
373}
374
375
376
377/*!
378 Constructs a custom bitmap cursor.
379
380 \a bitmap and
381 \a mask make up the bitmap.
382 \a hotX and
383 \a hotY define the cursor's hot spot.
384
385 If \a hotX is negative, it is set to the \c{bitmap().width()/2}.
386 If \a hotY is negative, it is set to the \c{bitmap().height()/2}.
387
388 The cursor \a bitmap (B) and \a mask (M) bits are combined like this:
389 \list
390 \li B=1 and M=1 gives black.
391 \li B=0 and M=1 gives white.
392 \li B=0 and M=0 gives transparent.
393 \li B=1 and M=0 gives an XOR'd result under Windows, undefined
394 results on all other platforms.
395 \endlist
396
397 Use the global Qt color Qt::color0 to draw 0-pixels and Qt::color1 to
398 draw 1-pixels in the bitmaps.
399
400 Valid cursor sizes depend on the display hardware (or the
401 underlying window system). We recommend using 32 x 32 cursors,
402 because this size is supported on all platforms. Some platforms
403 also support 16 x 16, 48 x 48, and 64 x 64 cursors.
404
405 \sa QBitmap::QBitmap(), QBitmap::setMask()
406*/
407
408QCursor::QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
409 : d(nullptr)
410{
411 d = QCursorData::setBitmap(bitmap, mask, hotX, hotY, devicePixelRatio: 1.0);
412}
413
414/*!
415 Constructs a cursor with the default arrow shape.
416*/
417QCursor::QCursor()
418{
419 if (!QCursorData::initialized) {
420 if (QCoreApplication::startingUp()) {
421 d = nullptr;
422 return;
423 }
424 QCursorData::initialize();
425 }
426 QCursorData *c = qt_cursorTable[0];
427 c->ref.ref();
428 d = c;
429}
430
431/*!
432 Constructs a cursor with the specified \a shape.
433
434 See \l Qt::CursorShape for a list of shapes.
435
436 \sa setShape()
437*/
438QCursor::QCursor(Qt::CursorShape shape)
439 : d(nullptr)
440{
441 QCursorData::initialize();
442 setShape(shape);
443}
444
445/*!
446 \fn bool QCursor::operator==(const QCursor &lhs, const QCursor &rhs)
447 \since 5.10
448
449 Equality operator. Returns \c true if \a lhs and \a rhs
450 have the same \l{QCursor::}{shape()} and, in the case of
451 \l{Qt::BitmapCursor}{bitmap cursors}, the same \l{QCursor::}{hotSpot()}
452 and either the same \l{QCursor::}{pixmap()} or the same
453 \l{QCursor::}{bitmap()} and \l{QCursor::}{mask()}.
454
455 \note When comparing bitmap cursors, this function only
456 compares the bitmaps' \l{QPixmap::cacheKey()}{cache keys},
457 not each pixel.
458
459 \sa operator!=(const QCursor &lhs, const QCursor &rhs)
460*/
461bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept
462{
463 if (lhs.d == rhs.d)
464 return true; // Copy or same shape
465
466 // Check pixmaps or bitmaps cache keys. Notice that having BitmapCursor
467 // shape implies either non-null pixmap or non-null bitmap and mask
468 if (lhs.shape() == Qt::BitmapCursor && rhs.shape() == Qt::BitmapCursor
469 && lhs.hotSpot() == rhs.hotSpot()) {
470 if (!lhs.d->pixmap.isNull())
471 return lhs.d->pixmap.cacheKey() == rhs.d->pixmap.cacheKey();
472
473 if (!rhs.d->pixmap.isNull())
474 return false;
475
476 return lhs.d->bm->cacheKey() == rhs.d->bm->cacheKey()
477 && lhs.d->bmm->cacheKey() == rhs.d->bmm->cacheKey();
478 }
479
480 return false;
481}
482
483/*!
484 \fn bool QCursor::operator!=(const QCursor &lhs, const QCursor &rhs)
485 \since 5.10
486
487 Inequality operator. Returns the equivalent of !(\a lhs == \a rhs).
488
489 \sa operator==(const QCursor &lhs, const QCursor &rhs)
490*/
491
492/*!
493 Returns the cursor shape identifier.
494
495 \sa setShape()
496*/
497Qt::CursorShape QCursor::shape() const
498{
499 QCursorData::initialize();
500 return d->cshape;
501}
502
503/*!
504 Sets the cursor to the shape identified by \a shape.
505
506 See \l Qt::CursorShape for the list of cursor shapes.
507
508 \sa shape()
509*/
510void QCursor::setShape(Qt::CursorShape shape)
511{
512 QCursorData::initialize();
513 QCursorData *c = uint(shape) <= Qt::LastCursor ? qt_cursorTable[shape] : nullptr;
514 if (!c)
515 c = qt_cursorTable[0];
516 c->ref.ref();
517 if (!d) {
518 d = c;
519 } else {
520 if (!d->ref.deref())
521 delete d;
522 d = c;
523 }
524}
525
526/*!
527 \fn QBitmap QCursor::bitmap(Qt::ReturnByValueConstant) const
528 \since 5.15
529 \deprecated Use the overload without argument instead.
530
531 Returns the cursor bitmap, or a null bitmap if it is one of the
532 standard cursors.
533
534 Previously, Qt provided a version of \c bitmap() which returned the bitmap
535 by-pointer. That version is now removed. To maintain compatibility
536 with old code, this function was provided to differentiate between the by-pointer
537 function and the by-value function.
538*/
539
540/*!
541 Returns the cursor bitmap, or a null bitmap if it is one of the
542 standard cursors.
543*/
544QBitmap QCursor::bitmap() const
545{
546 QCursorData::initialize();
547 if (d->bm)
548 return *(d->bm);
549 return QBitmap();
550}
551
552/*!
553 \fn QBitmap QCursor::mask(Qt::ReturnByValueConstant) const
554 \since 5.15
555 \deprecated Use the overload without argument instead.
556
557 Returns the cursor bitmap mask, or a null bitmap if it is one of the
558 standard cursors.
559
560 Previously, Qt provided a version of \c mask() which returned the bitmap
561 by-pointer. That version is now removed. To maintain compatibility
562 with old code, this function was provided to differentiate between the by-pointer
563 function and the by-value function.
564*/
565
566/*!
567 Returns the cursor bitmap mask, or a null bitmap if it is one of the
568 standard cursors.
569*/
570QBitmap QCursor::mask() const
571{
572 QCursorData::initialize();
573 if (d->bmm)
574 return *(d->bmm);
575 return QBitmap();
576}
577
578/*!
579 Returns the cursor pixmap. This is only valid if the cursor is a
580 pixmap cursor.
581*/
582
583QPixmap QCursor::pixmap() const
584{
585 QCursorData::initialize();
586 return d->pixmap;
587}
588
589/*!
590 Returns the cursor hot spot, or (0, 0) if it is one of the
591 standard cursors.
592*/
593
594QPoint QCursor::hotSpot() const
595{
596 QCursorData::initialize();
597 return QPoint(d->hx, d->hy);
598}
599
600/*!
601 Constructs a copy of the cursor \a c.
602*/
603
604QCursor::QCursor(const QCursor &c)
605{
606 QCursorData::initialize();
607 d = c.d;
608 d->ref.ref();
609}
610
611/*!
612 Destroys the cursor.
613*/
614
615QCursor::~QCursor()
616{
617 if (d && !d->ref.deref())
618 delete d;
619}
620
621
622/*!
623 Assigns \a c to this cursor and returns a reference to this
624 cursor.
625*/
626
627QCursor &QCursor::operator=(const QCursor &c)
628{
629 QCursorData::initialize();
630 if (c.d)
631 c.d->ref.ref();
632 if (d && !d->ref.deref())
633 delete d;
634 d = c.d;
635 return *this;
636}
637
638/*!
639 Returns the cursor as a QVariant.
640*/
641QCursor::operator QVariant() const
642{
643 return QVariant::fromValue(value: *this);
644}
645
646#ifndef QT_NO_DEBUG_STREAM
647QDebug operator<<(QDebug dbg, const QCursor &c)
648{
649 QDebugStateSaver saver(dbg);
650 dbg.nospace() << "QCursor(Qt::CursorShape(" << c.shape() << "))";
651 return dbg;
652}
653#endif
654
655/*****************************************************************************
656 Internal QCursorData class
657 *****************************************************************************/
658
659QCursorData *qt_cursorTable[Qt::LastCursor + 1];
660bool QCursorData::initialized = false;
661
662QCursorData::QCursorData(Qt::CursorShape s)
663 : ref(1), cshape(s), bm(nullptr), bmm(nullptr), hx(0), hy(0)
664{
665}
666
667QCursorData::~QCursorData()
668{
669 delete bm;
670 delete bmm;
671}
672
673/*! \internal */
674void QCursorData::cleanup()
675{
676 if (!QCursorData::initialized)
677 return;
678
679 for (int shape = 0; shape <= Qt::LastCursor; ++shape) {
680 // In case someone has a static QCursor defined with this shape
681 if (!qt_cursorTable[shape]->ref.deref())
682 delete qt_cursorTable[shape];
683 qt_cursorTable[shape] = nullptr;
684 }
685 QCursorData::initialized = false;
686}
687
688/*! \internal */
689void QCursorData::initialize()
690{
691 if (QCursorData::initialized)
692 return;
693 for (int shape = 0; shape <= Qt::LastCursor; ++shape)
694 qt_cursorTable[shape] = new QCursorData((Qt::CursorShape)shape);
695 QCursorData::initialized = true;
696}
697
698QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY, qreal devicePixelRatio)
699{
700 QCursorData::initialize();
701 if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
702 qWarning(msg: "QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
703 QCursorData *c = qt_cursorTable[0];
704 c->ref.ref();
705 return c;
706 }
707 QCursorData *d = new QCursorData;
708 d->bm = new QBitmap(bitmap);
709 d->bmm = new QBitmap(mask);
710 d->cshape = Qt::BitmapCursor;
711 d->hx = hotX >= 0 ? hotX : bitmap.width() / 2 / devicePixelRatio;
712 d->hy = hotY >= 0 ? hotY : bitmap.height() / 2 / devicePixelRatio;
713
714 return d;
715}
716
717void QCursorData::update()
718{
719}
720
721QT_END_NAMESPACE
722#endif // QT_NO_CURSOR
723
724

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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