| /**************************************************************************** |
| ** |
| ** 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 "qcocoascreen.h" |
| #include "qcocoahelpers.h" |
| #include <QtGui/private/qcoregraphics_p.h> |
| |
| #include <QtGui/QBitmap> |
| |
| QT_BEGIN_NAMESPACE |
| |
| QCocoaCursor::QCocoaCursor() |
| { |
| } |
| |
| QCocoaCursor::~QCocoaCursor() |
| { |
| // release cursors |
| QHash<Qt::CursorShape, NSCursor *>::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<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor); |
| } |
| |
| QPoint QCocoaCursor::pos() const |
| { |
| return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint(); |
| } |
| |
| void QCocoaCursor::setPos(const QPoint &position) |
| { |
| CGPoint pos; |
| pos.x = position.x(); |
| pos.y = position.y(); |
| |
| CGEventRef e = CGEventCreateMouseEvent(nullptr, kCGEventMouseMoved, pos, kCGMouseButtonLeft); |
| CGEventPost(kCGHIDEventTap, e); |
| CFRelease(e); |
| } |
| |
| |
| QSize QCocoaCursor::size() const |
| { |
| NSCursor *cocoaCursor = NSCursor.currentSystemCursor; |
| if (!cocoaCursor) |
| return QPlatformCursor::size(); |
| NSImage *cursorImage = cocoaCursor.image; |
| if (!cursorImage) |
| return QPlatformCursor::size(); |
| |
| QSizeF size = QSizeF::fromCGSize(cursorImage.size); |
| NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; |
| NSDictionary *accessSettings = [defaults persistentDomainForName:@"com.apple.universalaccess"]; |
| if (accessSettings == nil) |
| return size.toSize(); |
| |
| float sizeScale = [accessSettings[@"mouseDriverCursorSize"] floatValue]; |
| if (sizeScale > 0) { |
| size.rwidth() *= sizeScale; |
| size.rheight() *= sizeScale; |
| } |
| |
| return size.toSize(); |
| } |
| |
| NSCursor *QCocoaCursor::convertCursor(QCursor *cursor) |
| { |
| if (!cursor) |
| return nil; |
| |
| 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 = nil; |
| } |
| if (!cocoaCursor) { |
| cocoaCursor = createCursorData(cursor); |
| if (!cocoaCursor) |
| 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 = nullptr; |
| const uchar *cursorMaskData = nullptr; |
| 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 nil; |
| } |
| |
| // 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 nil; // 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<QRgb *>(bmi.scanLine(row)); |
| QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row)); |
| QRgb *finalData = reinterpret_cast<QRgb *>(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); |
| scaledPixmap.setDevicePixelRatio(1.0); |
| nsimage = static_cast<NSImage *>(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<NSImage *>(qt_mac_create_nsimage(pixmap)); |
| } |
| |
| NSCursor *nsCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; |
| [nsimage release]; |
| return nsCursor; |
| } |
| |
| QT_END_NAMESPACE |