5#include <AppKit/AppKit.h>
11#include <QtGui/private/qcoregraphics_p.h>
13#include <QtGui/QBitmap>
15#if !defined(QT_APPLE_NO_PRIVATE_APIS)
17+ (id)busyButClickableCursor;
19+ (
id)_windowResizeNorthWestSouthEastCursor;
20+ (
id)_windowResizeNorthEastSouthWestCursor;
21+ (
id)_windowResizeNorthSouthCursor;
22+ (
id)_windowResizeEastWestCursor;
28using namespace Qt::StringLiterals;
30QCocoaCursor::QCocoaCursor()
34QCocoaCursor::~QCocoaCursor()
37 QHash<Qt::CursorShape, NSCursor *>::const_iterator i = m_cursors.constBegin();
38 while (i != m_cursors.constEnd()) {
44void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
46 NSCursor *cocoaCursor = convertCursor(cursor);
48 if (QPlatformWindow * platformWindow = window->handle())
49 static_cast<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor);
52QPoint QCocoaCursor::pos()
const
54 return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint();
57void QCocoaCursor::setPos(
const QPoint &position)
63 CGEventRef e = CGEventCreateMouseEvent(
nullptr, kCGEventMouseMoved, pos, kCGMouseButtonLeft);
64 CGEventPost(kCGHIDEventTap, e);
69QSize QCocoaCursor::size()
const
71 NSCursor *cocoaCursor = NSCursor.currentSystemCursor;
73 return QPlatformCursor::size();
74 NSImage *cursorImage = cocoaCursor.image;
76 return QPlatformCursor::size();
78 QSizeF size = QSizeF::fromCGSize(cursorImage.size);
79 NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
80 NSDictionary *accessSettings = [defaults persistentDomainForName:@
"com.apple.universalaccess"];
81 if (accessSettings == nil)
84 float sizeScale = [accessSettings[@
"mouseDriverCursorSize"] floatValue];
86 size.rwidth() *= sizeScale;
87 size.rheight() *= sizeScale;
93NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
98 const Qt::CursorShape newShape = cursor->shape();
99 NSCursor *cocoaCursor = nil;
103 case Qt::ArrowCursor:
104 cocoaCursor= [NSCursor arrowCursor];
106 case Qt::ForbiddenCursor:
107 cocoaCursor = [NSCursor operationNotAllowedCursor];
109 case Qt::CrossCursor:
110 cocoaCursor = [NSCursor crosshairCursor];
112 case Qt::IBeamCursor:
113 cocoaCursor = [NSCursor IBeamCursor];
115 case Qt::WhatsThisCursor:
116#if !defined(QT_APPLE_NO_PRIVATE_APIS)
117 if ([NSCursor respondsToSelector:@selector(_helpCursor)]) {
118 cocoaCursor = [NSCursor performSelector:@selector(_helpCursor)];
124 case Qt::PointingHandCursor:
125 cocoaCursor = [NSCursor pointingHandCursor];
127 case Qt::SplitVCursor:
128 cocoaCursor = [NSCursor resizeUpDownCursor];
130 case Qt::SplitHCursor:
131 cocoaCursor = [NSCursor resizeLeftRightCursor];
133 case Qt::OpenHandCursor:
134 cocoaCursor = [NSCursor openHandCursor];
136 case Qt::ClosedHandCursor:
137 cocoaCursor = [NSCursor closedHandCursor];
139 case Qt::DragMoveCursor:
140 cocoaCursor = [NSCursor crosshairCursor];
142 case Qt::DragCopyCursor:
143 cocoaCursor = [NSCursor dragCopyCursor];
145 case Qt::DragLinkCursor:
146 cocoaCursor = [NSCursor dragLinkCursor];
149#if !defined(QT_APPLE_NO_PRIVATE_APIS)
150 if ([NSCursor respondsToSelector:@selector(busyButClickableCursor)])
151 cocoaCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
154 case Qt::SizeVerCursor:
155 case Qt::SizeHorCursor:
156 case Qt::SizeBDiagCursor:
157 case Qt::SizeFDiagCursor: {
158#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(150000
)
159 if (@available(macOS 15, *)) {
160 auto position = [newShape]{
162 case Qt::SizeVerCursor:
return NSCursorFrameResizePositionTop;
163 case Qt::SizeHorCursor:
return NSCursorFrameResizePositionLeft;
164 case Qt::SizeBDiagCursor:
return NSCursorFrameResizePositionTopRight;
165 case Qt::SizeFDiagCursor:
return NSCursorFrameResizePositionTopLeft;
166 default: Q_UNREACHABLE();
169 cocoaCursor = [NSCursor frameResizeCursorFromPosition:position
170 inDirections:NSCursorFrameResizeDirectionsAll];
174#if !defined(QT_APPLE_NO_PRIVATE_APIS)
175 auto selector = [newShape]{
177 case Qt::SizeVerCursor:
return @selector(_windowResizeNorthSouthCursor);
178 case Qt::SizeHorCursor:
return @selector(_windowResizeEastWestCursor);
179 case Qt::SizeBDiagCursor:
return @selector(_windowResizeNorthEastSouthWestCursor);
180 case Qt::SizeFDiagCursor:
return @selector(_windowResizeNorthWestSouthEastCursor);
181 default: Q_UNREACHABLE();
185 if ([NSCursor respondsToSelector:selector])
186 cocoaCursor = [NSCursor performSelector:selector];
197 cocoaCursor = m_cursors.value(newShape);
198 if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) {
199 [cocoaCursor release];
203 cocoaCursor = createCursorData(cursor);
205 return [NSCursor arrowCursor];
207 m_cursors.insert(newShape, cocoaCursor);
215NSCursor *QCocoaCursor::createCursorData(QCursor *cursor)
218
219
220
221
222
223
224#define QT_USE_APPROXIMATE_CURSORS
226 static const uchar cur_ver_bits[] = {
227 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
228 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0,
229 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 };
230 static const uchar mcur_ver_bits[] = {
231 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8,
232 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8,
233 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 };
235 static const uchar cur_hor_bits[] = {
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30,
237 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
239 static const uchar mcur_hor_bits[] = {
240 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78,
241 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78,
242 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 };
244 static const uchar cur_fdiag_bits[] = {
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78,
246 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00,
247 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
248 static const uchar mcur_fdiag_bits[] = {
249 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc,
250 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00,
251 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 };
253 static const uchar cur_bdiag_bits[] = {
254 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00,
255 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8,
256 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
257 static const uchar mcur_bdiag_bits[] = {
258 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04,
259 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc,
260 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 };
262 static const unsigned char cur_up_arrow_bits[] = {
263 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10,
264 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
265 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 };
266 static const unsigned char mcur_up_arrow_bits[] = {
267 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0,
268 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
269 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 };
271 const uchar *cursorData =
nullptr;
272 const uchar *cursorMaskData =
nullptr;
273 QPoint hotspot = cursor->hotSpot();
275 switch (cursor->shape()) {
276 case Qt::BitmapCursor: {
277 if (cursor->pixmap().isNull())
278 return createCursorFromBitmap(cursor->bitmap(), cursor->mask(), hotspot);
280 return createCursorFromPixmap(cursor->pixmap(), hotspot);
282 case Qt::BlankCursor: {
283 QPixmap pixmap = QPixmap(16, 16);
284 pixmap.fill(Qt::transparent);
285 return createCursorFromPixmap(pixmap);
287 case Qt::WaitCursor: {
288 QPixmap pixmap = QPixmap(
":/qt-project.org/mac/cursors/images/spincursor.png"_L1);
289 return createCursorFromPixmap(pixmap, hotspot);
291 case Qt::SizeAllCursor: {
292 QPixmap pixmap = QPixmap(
":/qt-project.org/mac/cursors/images/sizeallcursor.png"_L1);
293 return createCursorFromPixmap(pixmap, QPoint(8, 8));
295 case Qt::BusyCursor: {
296 QPixmap pixmap = QPixmap(
":/qt-project.org/mac/cursors/images/waitcursor.png"_L1);
297 return createCursorFromPixmap(pixmap, hotspot);
299#define QT_USE_APPROXIMATE_CURSORS
301 case Qt::SizeVerCursor:
302 cursorData = cur_ver_bits;
303 cursorMaskData = mcur_ver_bits;
304 hotspot = QPoint(8, 8);
306 case Qt::SizeHorCursor:
307 cursorData = cur_hor_bits;
308 cursorMaskData = mcur_hor_bits;
309 hotspot = QPoint(8, 8);
311 case Qt::SizeBDiagCursor:
312 cursorData = cur_fdiag_bits;
313 cursorMaskData = mcur_fdiag_bits;
314 hotspot = QPoint(8, 8);
316 case Qt::SizeFDiagCursor:
317 cursorData = cur_bdiag_bits;
318 cursorMaskData = mcur_bdiag_bits;
319 hotspot = QPoint(8, 8);
321 case Qt::UpArrowCursor:
322 cursorData = cur_up_arrow_bits;
323 cursorMaskData = mcur_up_arrow_bits;
324 hotspot = QPoint(8, 0);
328 qWarning(
"Qt: QCursor::update: Invalid cursor shape %d", cursor->shape());
334 QBitmap bitmap(QBitmap::fromData(QSize(16, 16), cursorData, QImage::Format_Mono));
335 QBitmap mask(QBitmap::fromData(QSize(16, 16), cursorMaskData, QImage::Format_Mono));
336 return (createCursorFromBitmap(bitmap, mask, hotspot));
342NSCursor *QCocoaCursor::createCursorFromBitmap(
const QBitmap &bitmap,
const QBitmap &mask,
const QPoint hotspot)
344 QImage finalCursor(bitmap.size(), QImage::Format_ARGB32);
345 QImage bmi = bitmap.toImage().convertToFormat(QImage::Format_RGB32);
346 QImage bmmi = mask.toImage().convertToFormat(QImage::Format_RGB32);
347 for (
int row = 0; row < finalCursor.height(); ++row) {
348 QRgb *bmData =
reinterpret_cast<QRgb *>(bmi.scanLine(row));
349 QRgb *bmmData =
reinterpret_cast<QRgb *>(bmmi.scanLine(row));
350 QRgb *finalData =
reinterpret_cast<QRgb *>(finalCursor.scanLine(row));
351 for (
int col = 0; col < finalCursor.width(); ++col) {
352 if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
353 finalData[col] = 0xffffffff;
354 }
else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
355 finalData[col] = 0x7f000000;
356 }
else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) {
357 finalData[col] = 0x00000000;
359 finalData[col] = 0xff000000;
364 return createCursorFromPixmap(QPixmap::fromImage(finalCursor), hotspot);
367NSCursor *QCocoaCursor::createCursorFromPixmap(
const QPixmap &pixmap,
const QPoint hotspot)
369 NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y());
370 auto *image = [NSImage imageFromQImage:pixmap.toImage()];
371 return [[NSCursor alloc] initWithImage:image hotSpot:hotSpot];
#define QT_USE_APPROXIMATE_CURSORS