1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the plugins of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include "qcocoacursor.h" 41#include "qcocoawindow.h" 42#include "qcocoascreen.h" 43#include "qcocoahelpers.h" 44#include <QtGui/private/qcoregraphics_p.h> 45 46#include <QtGui/QBitmap> 47 48QT_BEGIN_NAMESPACE 49 50QCocoaCursor::QCocoaCursor() 51{ 52} 53 54QCocoaCursor::~QCocoaCursor() 55{ 56 // release cursors 57 QHash<Qt::CursorShape, NSCursor *>::const_iterator i = m_cursors.constBegin(); 58 while (i != m_cursors.constEnd()) { 59 [*i release]; 60 ++i; 61 } 62} 63 64void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) 65{ 66 NSCursor *cocoaCursor = convertCursor(cursor); 67 68 if (QPlatformWindow * platformWindow = window->handle()) 69 static_cast<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor); 70} 71 72QPoint QCocoaCursor::pos() const 73{ 74 return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint(); 75} 76 77void QCocoaCursor::setPos(const QPoint &position) 78{ 79 CGPoint pos; 80 pos.x = position.x(); 81 pos.y = position.y(); 82 83 CGEventRef e = CGEventCreateMouseEvent(nullptr, kCGEventMouseMoved, pos, kCGMouseButtonLeft); 84 CGEventPost(kCGHIDEventTap, e); 85 CFRelease(e); 86} 87 88 89QSize QCocoaCursor::size() const 90{ 91 NSCursor *cocoaCursor = NSCursor.currentSystemCursor; 92 if (!cocoaCursor) 93 return QPlatformCursor::size(); 94 NSImage *cursorImage = cocoaCursor.image; 95 if (!cursorImage) 96 return QPlatformCursor::size(); 97 98 QSizeF size = QSizeF::fromCGSize(cursorImage.size); 99 NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; 100 NSDictionary *accessSettings = [defaults persistentDomainForName:@"com.apple.universalaccess"]; 101 if (accessSettings == nil) 102 return size.toSize(); 103 104 float sizeScale = [accessSettings[@"mouseDriverCursorSize"] floatValue]; 105 if (sizeScale > 0) { 106 size.rwidth() *= sizeScale; 107 size.rheight() *= sizeScale; 108 } 109 110 return size.toSize(); 111} 112 113NSCursor *QCocoaCursor::convertCursor(QCursor *cursor) 114{ 115 if (!cursor) 116 return nil; 117 118 const Qt::CursorShape newShape = cursor->shape(); 119 NSCursor *cocoaCursor; 120 121 // Check for a suitable built-in NSCursor first: 122 switch (newShape) { 123 case Qt::ArrowCursor: 124 cocoaCursor= [NSCursor arrowCursor]; 125 break; 126 case Qt::ForbiddenCursor: 127 cocoaCursor = [NSCursor operationNotAllowedCursor]; 128 break; 129 case Qt::CrossCursor: 130 cocoaCursor = [NSCursor crosshairCursor]; 131 break; 132 case Qt::IBeamCursor: 133 cocoaCursor = [NSCursor IBeamCursor]; 134 break; 135 case Qt::WhatsThisCursor: //for now just use the pointing hand 136 case Qt::PointingHandCursor: 137 cocoaCursor = [NSCursor pointingHandCursor]; 138 break; 139 case Qt::SplitVCursor: 140 cocoaCursor = [NSCursor resizeUpDownCursor]; 141 break; 142 case Qt::SplitHCursor: 143 cocoaCursor = [NSCursor resizeLeftRightCursor]; 144 break; 145 case Qt::OpenHandCursor: 146 cocoaCursor = [NSCursor openHandCursor]; 147 break; 148 case Qt::ClosedHandCursor: 149 cocoaCursor = [NSCursor closedHandCursor]; 150 break; 151 case Qt::DragMoveCursor: 152 cocoaCursor = [NSCursor crosshairCursor]; 153 break; 154 case Qt::DragCopyCursor: 155 cocoaCursor = [NSCursor dragCopyCursor]; 156 break; 157 case Qt::DragLinkCursor: 158 cocoaCursor = [NSCursor dragLinkCursor]; 159 break; 160 default : { 161 // No suitable OS cursor exist, use cursors provided 162 // by Qt for the rest. Check for a cached cursor: 163 cocoaCursor = m_cursors.value(newShape); 164 if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { 165 [cocoaCursor release]; 166 cocoaCursor = nil; 167 } 168 if (!cocoaCursor) { 169 cocoaCursor = createCursorData(cursor); 170 if (!cocoaCursor) 171 return [NSCursor arrowCursor]; 172 173 m_cursors.insert(newShape, cocoaCursor); 174 } 175 176 break; } 177 } 178 return cocoaCursor; 179} 180 181 182// Creates an NSCursor for the given QCursor. 183NSCursor *QCocoaCursor::createCursorData(QCursor *cursor) 184{ 185 /* Note to self... *** 186 * mask x data 187 * 0xFF x 0x00 == fully opaque white 188 * 0x00 x 0xFF == xor'd black 189 * 0xFF x 0xFF == fully opaque black 190 * 0x00 x 0x00 == fully transparent 191 */ 192#define QT_USE_APPROXIMATE_CURSORS 193#ifdef QT_USE_APPROXIMATE_CURSORS 194 static const uchar cur_ver_bits[] = { 195 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 196 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0, 197 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 }; 198 static const uchar mcur_ver_bits[] = { 199 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8, 200 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8, 201 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 }; 202 203 static const uchar cur_hor_bits[] = { 204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30, 205 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20, 206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 207 static const uchar mcur_hor_bits[] = { 208 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78, 209 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78, 210 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 }; 211 212 static const uchar cur_fdiag_bits[] = { 213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78, 214 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00, 215 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 }; 216 static const uchar mcur_fdiag_bits[] = { 217 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc, 218 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00, 219 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 }; 220 221 static const uchar cur_bdiag_bits[] = { 222 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00, 223 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8, 224 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 225 static const uchar mcur_bdiag_bits[] = { 226 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04, 227 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc, 228 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 }; 229 230 static const unsigned char cur_up_arrow_bits[] = { 231 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10, 232 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 233 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 }; 234 static const unsigned char mcur_up_arrow_bits[] = { 235 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0, 236 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 237 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 }; 238#endif 239 const uchar *cursorData = nullptr; 240 const uchar *cursorMaskData = nullptr; 241 QPoint hotspot = cursor->hotSpot(); 242 243 switch (cursor->shape()) { 244 case Qt::BitmapCursor: { 245 if (cursor->pixmap().isNull()) 246 return createCursorFromBitmap(cursor->bitmap(Qt::ReturnByValue), cursor->mask(Qt::ReturnByValue), hotspot); 247 else 248 return createCursorFromPixmap(cursor->pixmap(), hotspot); 249 break; } 250 case Qt::BlankCursor: { 251 QPixmap pixmap = QPixmap(16, 16); 252 pixmap.fill(Qt::transparent); 253 return createCursorFromPixmap(pixmap); 254 break; } 255 case Qt::WaitCursor: { 256 QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/spincursor.png")); 257 return createCursorFromPixmap(pixmap, hotspot); 258 break; } 259 case Qt::SizeAllCursor: { 260 QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/sizeallcursor.png")); 261 return createCursorFromPixmap(pixmap, QPoint(8, 8)); 262 break; } 263 case Qt::BusyCursor: { 264 QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/waitcursor.png")); 265 return createCursorFromPixmap(pixmap, hotspot); 266 break; } 267#define QT_USE_APPROXIMATE_CURSORS 268#ifdef QT_USE_APPROXIMATE_CURSORS 269 case Qt::SizeVerCursor: 270 cursorData = cur_ver_bits; 271 cursorMaskData = mcur_ver_bits; 272 hotspot = QPoint(8, 8); 273 break; 274 case Qt::SizeHorCursor: 275 cursorData = cur_hor_bits; 276 cursorMaskData = mcur_hor_bits; 277 hotspot = QPoint(8, 8); 278 break; 279 case Qt::SizeBDiagCursor: 280 cursorData = cur_fdiag_bits; 281 cursorMaskData = mcur_fdiag_bits; 282 hotspot = QPoint(8, 8); 283 break; 284 case Qt::SizeFDiagCursor: 285 cursorData = cur_bdiag_bits; 286 cursorMaskData = mcur_bdiag_bits; 287 hotspot = QPoint(8, 8); 288 break; 289 case Qt::UpArrowCursor: 290 cursorData = cur_up_arrow_bits; 291 cursorMaskData = mcur_up_arrow_bits; 292 hotspot = QPoint(8, 0); 293 break; 294#endif 295 default: 296 qWarning("Qt: QCursor::update: Invalid cursor shape %d", cursor->shape()); 297 return nil; 298 } 299 300 // Create an NSCursor from image data if this a self-provided cursor. 301 if (cursorData) { 302 QBitmap bitmap(QBitmap::fromData(QSize(16, 16), cursorData, QImage::Format_Mono)); 303 QBitmap mask(QBitmap::fromData(QSize(16, 16), cursorMaskData, QImage::Format_Mono)); 304 return (createCursorFromBitmap(bitmap, mask, hotspot)); 305 } 306 307 return nil; // should not happen, all cases covered above 308} 309 310NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap &bitmap, const QBitmap &mask, const QPoint hotspot) 311{ 312 QImage finalCursor(bitmap.size(), QImage::Format_ARGB32); 313 QImage bmi = bitmap.toImage().convertToFormat(QImage::Format_RGB32); 314 QImage bmmi = mask.toImage().convertToFormat(QImage::Format_RGB32); 315 for (int row = 0; row < finalCursor.height(); ++row) { 316 QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row)); 317 QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row)); 318 QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row)); 319 for (int col = 0; col < finalCursor.width(); ++col) { 320 if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { 321 finalData[col] = 0xffffffff; 322 } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { 323 finalData[col] = 0x7f000000; 324 } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) { 325 finalData[col] = 0x00000000; 326 } else { 327 finalData[col] = 0xff000000; 328 } 329 } 330 } 331 332 return createCursorFromPixmap(QPixmap::fromImage(finalCursor), hotspot); 333} 334 335NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap &pixmap, const QPoint hotspot) 336{ 337 NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y()); 338 auto *image = [NSImage imageFromQImage:pixmap.toImage()]; 339 return [[NSCursor alloc] initWithImage:image hotSpot:hotSpot]; 340} 341 342QT_END_NAMESPACE 343