/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcocoacursor.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" #include #include QT_BEGIN_NAMESPACE QCocoaCursor::QCocoaCursor() { } QCocoaCursor::~QCocoaCursor() { // release cursors QHash::const_iterator i = m_cursors.constBegin(); while (i != m_cursors.constEnd()) { [*i release]; ++i; } } void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) { NSCursor *cocoaCursor = convertCursor(cursor); if (QPlatformWindow * platformWindow = window->handle()) static_cast(platformWindow)->setWindowCursor(cocoaCursor); } QPoint QCocoaCursor::pos() const { return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint(); } void QCocoaCursor::setPos(const QPoint &position) { CGPoint pos; pos.x = position.x(); pos.y = position.y(); CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft); CGEventPost(kCGHIDEventTap, e); CFRelease(e); } NSCursor *QCocoaCursor::convertCursor(QCursor *cursor) { if (cursor == Q_NULLPTR) return 0; const Qt::CursorShape newShape = cursor->shape(); NSCursor *cocoaCursor; // Check for a suitable built-in NSCursor first: switch (newShape) { case Qt::ArrowCursor: cocoaCursor= [NSCursor arrowCursor]; break; case Qt::ForbiddenCursor: cocoaCursor = [NSCursor operationNotAllowedCursor]; break; case Qt::CrossCursor: cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::IBeamCursor: cocoaCursor = [NSCursor IBeamCursor]; break; case Qt::WhatsThisCursor: //for now just use the pointing hand case Qt::PointingHandCursor: cocoaCursor = [NSCursor pointingHandCursor]; break; case Qt::SplitVCursor: cocoaCursor = [NSCursor resizeUpDownCursor]; break; case Qt::SplitHCursor: cocoaCursor = [NSCursor resizeLeftRightCursor]; break; case Qt::OpenHandCursor: cocoaCursor = [NSCursor openHandCursor]; break; case Qt::ClosedHandCursor: cocoaCursor = [NSCursor closedHandCursor]; break; case Qt::DragMoveCursor: cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::DragCopyCursor: cocoaCursor = [NSCursor dragCopyCursor]; break; case Qt::DragLinkCursor: cocoaCursor = [NSCursor dragLinkCursor]; break; default : { // No suitable OS cursor exist, use cursors provided // by Qt for the rest. Check for a cached cursor: cocoaCursor = m_cursors.value(newShape); if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { [cocoaCursor release]; cocoaCursor = 0; } if (cocoaCursor == 0) { cocoaCursor = createCursorData(cursor); if (cocoaCursor == 0) return [NSCursor arrowCursor]; m_cursors.insert(newShape, cocoaCursor); } break; } } return cocoaCursor; } // Creates an NSCursor for the given QCursor. NSCursor *QCocoaCursor::createCursorData(QCursor *cursor) { /* Note to self... *** * mask x data * 0xFF x 0x00 == fully opaque white * 0x00 x 0xFF == xor'd black * 0xFF x 0xFF == fully opaque black * 0x00 x 0x00 == fully transparent */ #define QT_USE_APPROXIMATE_CURSORS #ifdef QT_USE_APPROXIMATE_CURSORS static const uchar cur_ver_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 }; static const uchar mcur_ver_bits[] = { 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8, 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 }; static const uchar cur_hor_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30, 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const uchar mcur_hor_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78, 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78, 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 }; static const uchar cur_fdiag_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const uchar mcur_fdiag_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc, 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00, 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 }; static const uchar cur_bdiag_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00, 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const uchar mcur_bdiag_bits[] = { 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04, 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char cur_up_arrow_bits[] = { 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10, 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 }; static const unsigned char mcur_up_arrow_bits[] = { 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0, 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 }; #endif const uchar *cursorData = 0; const uchar *cursorMaskData = 0; QPoint hotspot = cursor->hotSpot(); switch (cursor->shape()) { case Qt::BitmapCursor: { if (cursor->pixmap().isNull()) return createCursorFromBitmap(cursor->bitmap(), cursor->mask(), hotspot); else return createCursorFromPixmap(cursor->pixmap(), hotspot); break; } case Qt::BlankCursor: { QPixmap pixmap = QPixmap(16, 16); pixmap.fill(Qt::transparent); return createCursorFromPixmap(pixmap); break; } case Qt::WaitCursor: { QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/spincursor.png")); return createCursorFromPixmap(pixmap, hotspot); break; } case Qt::SizeAllCursor: { QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/sizeallcursor.png")); return createCursorFromPixmap(pixmap, QPoint(8, 8)); break; } case Qt::BusyCursor: { QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/waitcursor.png")); return createCursorFromPixmap(pixmap, hotspot); break; } #define QT_USE_APPROXIMATE_CURSORS #ifdef QT_USE_APPROXIMATE_CURSORS case Qt::SizeVerCursor: cursorData = cur_ver_bits; cursorMaskData = mcur_ver_bits; hotspot = QPoint(8, 8); break; case Qt::SizeHorCursor: cursorData = cur_hor_bits; cursorMaskData = mcur_hor_bits; hotspot = QPoint(8, 8); break; case Qt::SizeBDiagCursor: cursorData = cur_fdiag_bits; cursorMaskData = mcur_fdiag_bits; hotspot = QPoint(8, 8); break; case Qt::SizeFDiagCursor: cursorData = cur_bdiag_bits; cursorMaskData = mcur_bdiag_bits; hotspot = QPoint(8, 8); break; case Qt::UpArrowCursor: cursorData = cur_up_arrow_bits; cursorMaskData = mcur_up_arrow_bits; hotspot = QPoint(8, 0); break; #endif default: qWarning("Qt: QCursor::update: Invalid cursor shape %d", cursor->shape()); return 0; } // Create an NSCursor from image data if this a self-provided cursor. if (cursorData) { QBitmap bitmap(QBitmap::fromData(QSize(16, 16), cursorData, QImage::Format_Mono)); QBitmap mask(QBitmap::fromData(QSize(16, 16), cursorMaskData, QImage::Format_Mono)); return (createCursorFromBitmap(&bitmap, &mask, hotspot)); } return 0; // should not happen, all cases covered above } NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap *bitmap, const QBitmap *mask, const QPoint hotspot) { QImage finalCursor(bitmap->size(), QImage::Format_ARGB32); QImage bmi = bitmap->toImage().convertToFormat(QImage::Format_RGB32); QImage bmmi = mask->toImage().convertToFormat(QImage::Format_RGB32); for (int row = 0; row < finalCursor.height(); ++row) { QRgb *bmData = reinterpret_cast(bmi.scanLine(row)); QRgb *bmmData = reinterpret_cast(bmmi.scanLine(row)); QRgb *finalData = reinterpret_cast(finalCursor.scanLine(row)); for (int col = 0; col < finalCursor.width(); ++col) { if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { finalData[col] = 0xffffffff; } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { finalData[col] = 0x7f000000; } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) { finalData[col] = 0x00000000; } else { finalData[col] = 0xff000000; } } } return createCursorFromPixmap(QPixmap::fromImage(finalCursor), hotspot); } NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot) { NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y()); NSImage *nsimage; if (pixmap.devicePixelRatio() > 1.0) { QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio(); QPixmap scaledPixmap = pixmap.scaled(layoutSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); nsimage = static_cast(qt_mac_create_nsimage(scaledPixmap)); CGImageRef cgImage = qt_mac_toCGImage(pixmap.toImage()); NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; [nsimage addRepresentation:imageRep]; [imageRep release]; CGImageRelease(cgImage); } else { nsimage = static_cast(qt_mac_create_nsimage(pixmap)); } NSCursor *nsCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; [nsimage release]; return nsCursor; } QT_END_NAMESPACE