blob: e0d623fc4c69779d05fdcc1f8b22b5f65f8c2f25 [file] [log] [blame]
/****************************************************************************
**
** 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