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
qcocoacursor.mm
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#include <AppKit/AppKit.h>
5
6#include "qcocoacursor.h"
7#include "qcocoawindow.h"
8#include "qcocoascreen.h"
9#include "qcocoahelpers.h"
10#include <QtGui/private/qcoregraphics_p.h>
11
12#include <QtGui/QBitmap>
13
14#if !defined(QT_APPLE_NO_PRIVATE_APIS)
15@interface NSCursor()
16+ (id)_windowResizeNorthWestSouthEastCursor;
17+ (id)_windowResizeNorthEastSouthWestCursor;
18+ (id)_windowResizeNorthSouthCursor;
19+ (id)_windowResizeEastWestCursor;
20@end
21#endif // QT_APPLE_NO_PRIVATE_APIS
22
24
25using namespace Qt::StringLiterals;
26
30
32{
33 // release cursors
35 while (i != m_cursors.constEnd()) {
36 [*i release];
37 ++i;
38 }
39}
40
42{
43 NSCursor *cocoaCursor = convertCursor(cursor);
44
45 if (QPlatformWindow * platformWindow = window->handle())
46 static_cast<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor);
47}
48
50{
51 return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint();
52}
53
55{
56 CGPoint pos;
57 pos.x = position.x();
58 pos.y = position.y();
59
60 CGEventRef e = CGEventCreateMouseEvent(nullptr, kCGEventMouseMoved, pos, kCGMouseButtonLeft);
61 CGEventPost(kCGHIDEventTap, e);
62 CFRelease(e);
63}
64
65
67{
68 NSCursor *cocoaCursor = NSCursor.currentSystemCursor;
69 if (!cocoaCursor)
70 return QPlatformCursor::size();
71 NSImage *cursorImage = cocoaCursor.image;
72 if (!cursorImage)
73 return QPlatformCursor::size();
74
75 QSizeF size = QSizeF::fromCGSize(cursorImage.size);
76 NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
77 NSDictionary *accessSettings = [defaults persistentDomainForName:@"com.apple.universalaccess"];
78 if (accessSettings == nil)
79 return size.toSize();
80
81 float sizeScale = [accessSettings[@"mouseDriverCursorSize"] floatValue];
82 if (sizeScale > 0) {
83 size.rwidth() *= sizeScale;
84 size.rheight() *= sizeScale;
85 }
86
87 return size.toSize();
88}
89
90NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
91{
92 if (!cursor)
93 return nil;
94
95 const Qt::CursorShape newShape = cursor->shape();
96 NSCursor *cocoaCursor = nil;
97
98 // Check for a suitable built-in NSCursor first:
99 switch (newShape) {
100 case Qt::ArrowCursor:
101 cocoaCursor= [NSCursor arrowCursor];
102 break;
104 cocoaCursor = [NSCursor operationNotAllowedCursor];
105 break;
106 case Qt::CrossCursor:
107 cocoaCursor = [NSCursor crosshairCursor];
108 break;
109 case Qt::IBeamCursor:
110 cocoaCursor = [NSCursor IBeamCursor];
111 break;
112 case Qt::WhatsThisCursor: //for now just use the pointing hand
114 cocoaCursor = [NSCursor pointingHandCursor];
115 break;
116 case Qt::SplitVCursor:
117 cocoaCursor = [NSCursor resizeUpDownCursor];
118 break;
119 case Qt::SplitHCursor:
120 cocoaCursor = [NSCursor resizeLeftRightCursor];
121 break;
123 cocoaCursor = [NSCursor openHandCursor];
124 break;
126 cocoaCursor = [NSCursor closedHandCursor];
127 break;
129 cocoaCursor = [NSCursor crosshairCursor];
130 break;
132 cocoaCursor = [NSCursor dragCopyCursor];
133 break;
135 cocoaCursor = [NSCursor dragLinkCursor];
136 break;
137#if !defined(QT_APPLE_NO_PRIVATE_APIS)
139 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)])
140 cocoaCursor = [NSCursor _windowResizeNorthSouthCursor];
141 break;
143 if ([NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)])
144 cocoaCursor = [NSCursor _windowResizeEastWestCursor];
145 break;
147 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)])
148 cocoaCursor = [NSCursor _windowResizeNorthEastSouthWestCursor];
149 break;
151 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)])
152 cocoaCursor = [NSCursor _windowResizeNorthWestSouthEastCursor];
153 break;
154#endif // QT_APPLE_NO_PRIVATE_APIS
155 default:
156 break;
157 }
158
159 if (!cocoaCursor) {
160 // No suitable OS cursor exist, use cursors provided
161 // by Qt for the rest. Check for a cached cursor:
162 cocoaCursor = m_cursors.value(newShape);
163 if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) {
164 [cocoaCursor release];
165 cocoaCursor = nil;
166 }
167 if (!cocoaCursor) {
168 cocoaCursor = createCursorData(cursor);
169 if (!cocoaCursor)
170 return [NSCursor arrowCursor];
171
172 m_cursors.insert(newShape, cocoaCursor);
173 }
174 }
175 return cocoaCursor;
176}
177
178
179// Creates an NSCursor for the given QCursor.
180NSCursor *QCocoaCursor::createCursorData(QCursor *cursor)
181{
182 /* Note to self... ***
183 * mask x data
184 * 0xFF x 0x00 == fully opaque white
185 * 0x00 x 0xFF == xor'd black
186 * 0xFF x 0xFF == fully opaque black
187 * 0x00 x 0x00 == fully transparent
188 */
189#define QT_USE_APPROXIMATE_CURSORS
190#ifdef QT_USE_APPROXIMATE_CURSORS
191 static const uchar cur_ver_bits[] = {
192 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
193 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0,
194 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 };
195 static const uchar mcur_ver_bits[] = {
196 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8,
197 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8,
198 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 };
199
200 static const uchar cur_hor_bits[] = {
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30,
202 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20,
203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
204 static const uchar mcur_hor_bits[] = {
205 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78,
206 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78,
207 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 };
208
209 static const uchar cur_fdiag_bits[] = {
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78,
211 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00,
212 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
213 static const uchar mcur_fdiag_bits[] = {
214 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc,
215 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00,
216 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 };
217
218 static const uchar cur_bdiag_bits[] = {
219 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00,
220 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8,
221 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
222 static const uchar mcur_bdiag_bits[] = {
223 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04,
224 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc,
225 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 };
226
227 static const unsigned char cur_up_arrow_bits[] = {
228 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10,
229 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
230 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 };
231 static const unsigned char mcur_up_arrow_bits[] = {
232 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0,
233 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
234 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 };
235#endif
236 const uchar *cursorData = nullptr;
237 const uchar *cursorMaskData = nullptr;
238 QPoint hotspot = cursor->hotSpot();
239
240 switch (cursor->shape()) {
241 case Qt::BitmapCursor: {
242 if (cursor->pixmap().isNull())
243 return createCursorFromBitmap(cursor->bitmap(), cursor->mask(), hotspot);
244 else
245 return createCursorFromPixmap(cursor->pixmap(), hotspot);
246 break; }
247 case Qt::BlankCursor: {
248 QPixmap pixmap = QPixmap(16, 16);
250 return createCursorFromPixmap(pixmap);
251 break; }
252 case Qt::WaitCursor: {
253 QPixmap pixmap = QPixmap(":/qt-project.org/mac/cursors/images/spincursor.png"_L1);
254 return createCursorFromPixmap(pixmap, hotspot);
255 break; }
256 case Qt::SizeAllCursor: {
257 QPixmap pixmap = QPixmap(":/qt-project.org/mac/cursors/images/sizeallcursor.png"_L1);
258 return createCursorFromPixmap(pixmap, QPoint(8, 8));
259 break; }
260 case Qt::BusyCursor: {
261 QPixmap pixmap = QPixmap(":/qt-project.org/mac/cursors/images/waitcursor.png"_L1);
262 return createCursorFromPixmap(pixmap, hotspot);
263 break; }
264#define QT_USE_APPROXIMATE_CURSORS
265#ifdef QT_USE_APPROXIMATE_CURSORS
267 cursorData = cur_ver_bits;
268 cursorMaskData = mcur_ver_bits;
269 hotspot = QPoint(8, 8);
270 break;
272 cursorData = cur_hor_bits;
273 cursorMaskData = mcur_hor_bits;
274 hotspot = QPoint(8, 8);
275 break;
277 cursorData = cur_fdiag_bits;
278 cursorMaskData = mcur_fdiag_bits;
279 hotspot = QPoint(8, 8);
280 break;
282 cursorData = cur_bdiag_bits;
283 cursorMaskData = mcur_bdiag_bits;
284 hotspot = QPoint(8, 8);
285 break;
287 cursorData = cur_up_arrow_bits;
288 cursorMaskData = mcur_up_arrow_bits;
289 hotspot = QPoint(8, 0);
290 break;
291#endif
292 default:
293 qWarning("Qt: QCursor::update: Invalid cursor shape %d", cursor->shape());
294 return nil;
295 }
296
297 // Create an NSCursor from image data if this a self-provided cursor.
298 if (cursorData) {
300 QBitmap mask(QBitmap::fromData(QSize(16, 16), cursorMaskData, QImage::Format_Mono));
301 return (createCursorFromBitmap(bitmap, mask, hotspot));
302 }
303
304 return nil; // should not happen, all cases covered above
305}
306
307NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap &bitmap, const QBitmap &mask, const QPoint hotspot)
308{
309 QImage finalCursor(bitmap.size(), QImage::Format_ARGB32);
312 for (int row = 0; row < finalCursor.height(); ++row) {
313 QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row));
314 QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row));
315 QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row));
316 for (int col = 0; col < finalCursor.width(); ++col) {
317 if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
318 finalData[col] = 0xffffffff;
319 } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
320 finalData[col] = 0x7f000000;
321 } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) {
322 finalData[col] = 0x00000000;
323 } else {
324 finalData[col] = 0xff000000;
325 }
326 }
327 }
328
329 return createCursorFromPixmap(QPixmap::fromImage(finalCursor), hotspot);
330}
331
332NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap &pixmap, const QPoint hotspot)
333{
334 NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y());
335 auto *image = [NSImage imageFromQImage:pixmap.toImage()];
336 return [[NSCursor alloc] initWithImage:image hotSpot:hotSpot];
337}
338
\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
void setPos(const QPoint &position) override
void changeCursor(QCursor *cursor, QWindow *window) override
This method is called by Qt whenever the cursor graphic should be changed.
QSize size() const override
Returns the size of the cursor, in native pixels.
QPoint pos() const override
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
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
QPixmap pixmap() const
Returns the cursor pixmap.
Definition qcursor.cpp:584
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
\inmodule QtCore
Definition qhash.h:1145
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
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1215
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtGui
Definition qimage.h:37
@ Format_RGB32
Definition qimage.h:46
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32
Definition qimage.h:47
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const &
Definition qimage.h:125
Native interface for QPlatformWindow on \macos. \inmodule QtGui.
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
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
virtual QSize size() const
Returns the size of the cursor, in native pixels.
The QPlatformWindow class provides an abstraction for top-level windows.
\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
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
\inmodule QtGui
Definition qwindow.h:63
QCursor cursor
Combined button and popup list for selecting options.
CursorShape
@ BlankCursor
@ CrossCursor
@ DragCopyCursor
@ BitmapCursor
@ PointingHandCursor
@ SizeHorCursor
@ SizeAllCursor
@ WaitCursor
@ SizeVerCursor
@ DragLinkCursor
@ OpenHandCursor
@ SizeFDiagCursor
@ WhatsThisCursor
@ ArrowCursor
@ SplitVCursor
@ UpArrowCursor
@ ClosedHandCursor
@ DragMoveCursor
@ IBeamCursor
@ SizeBDiagCursor
@ ForbiddenCursor
@ BusyCursor
@ SplitHCursor
@ transparent
Definition qnamespace.h:47
Definition image.cpp:4
#define qWarning
Definition qlogging.h:166
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint id
[7]
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLsizei GLfixed GLfixed GLfixed GLfixed const GLubyte * bitmap
GLenum GLenum GLsizei void * row
static const unsigned char mcur_up_arrow_bits[]
static const uchar cur_fdiag_bits[]
static const unsigned char cur_up_arrow_bits[]
static const uchar mcur_bdiag_bits[]
static const uchar cur_ver_bits[]
static const uchar mcur_ver_bits[]
static const uchar cur_bdiag_bits[]
static const uchar mcur_fdiag_bits[]
static const uchar mcur_hor_bits[]
static const uchar cur_hor_bits[]
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
unsigned char uchar
Definition qtypes.h:32
QFile defaults(defaultsPath)
QFileSelector selector
[1]
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]