diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoaintegration.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintegration.mm | 274 |
1 files changed, 58 insertions, 216 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index bac49cfad9..55b3805df3 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -51,10 +51,12 @@ #include "qcocoainputcontext.h" #include "qcocoamimetypes.h" #include "qcocoaaccessibility.h" +#include "qcocoascreen.h" #include <qpa/qplatforminputcontextfactory_p.h> #include <qpa/qplatformaccessibility.h> #include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qplatformoffscreensurface.h> #include <QtCore/qcoreapplication.h> #include <QtGui/private/qcoregraphics_p.h> @@ -78,221 +80,6 @@ QT_BEGIN_NAMESPACE class QCoreTextFontEngine; class QFontEngineFT; -QCocoaScreen::QCocoaScreen(int screenIndex) - : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0) -{ - updateGeometry(); - m_cursor = new QCocoaCursor; -} - -QCocoaScreen::~QCocoaScreen() -{ - delete m_cursor; -} - -NSScreen *QCocoaScreen::nativeScreen() const -{ - NSArray *screens = [NSScreen screens]; - - // Stale reference, screen configuration has changed - if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count]) - return nil; - - return [screens objectAtIndex:m_screenIndex]; -} - -/*! - Flips the Y coordinate of the point between quadrant I and IV. - - The native coordinate system on macOS uses quadrant I, with origin - in bottom left, and Qt uses quadrant IV, with origin in top left. - - By flippig the Y coordinate, we can map the position between the - two coordinate systems. -*/ -QPointF QCocoaScreen::flipCoordinate(const QPointF &pos) const -{ - return QPointF(pos.x(), m_geometry.height() - pos.y()); -} - -/*! - Flips the Y coordinate of the rectangle between quadrant I and IV. - - The native coordinate system on macOS uses quadrant I, with origin - in bottom left, and Qt uses quadrant IV, with origin in top left. - - By flippig the Y coordinate, we can map the rectangle between the - two coordinate systems. -*/ -QRectF QCocoaScreen::flipCoordinate(const QRectF &rect) const -{ - return QRectF(flipCoordinate(rect.topLeft() + QPoint(0, rect.height())), rect.size()); -} - -void QCocoaScreen::updateGeometry() -{ - NSScreen *nsScreen = nativeScreen(); - if (!nsScreen) - return; - - // At this point the geometry is in native coordinates, but the size - // is correct, which we take advantage of next when we map the native - // coordinates to the Qt coordinate system. - m_geometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.frame)).toRect(); - m_availableGeometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.visibleFrame)).toRect(); - - // The reference screen for the geometry is always the primary screen, but since - // we may be in the process of creating and registering the primary screen, we - // must special-case that and assign it direcly. - QCocoaScreen *primaryScreen = (nsScreen == [[NSScreen screens] firstObject]) ? - this : static_cast<QCocoaScreen*>(QGuiApplication::primaryScreen()->handle()); - - m_geometry = primaryScreen->mapFromNative(m_geometry).toRect(); - m_availableGeometry = primaryScreen->mapFromNative(m_availableGeometry).toRect(); - - m_format = QImage::Format_RGB32; - m_depth = NSBitsPerPixelFromDepth([nsScreen depth]); - - NSDictionary *devDesc = [nsScreen deviceDescription]; - CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue]; - CGSize size = CGDisplayScreenSize(dpy); - m_physicalSize = QSizeF(size.width, size.height); - m_logicalDpi.first = 72; - m_logicalDpi.second = 72; - CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy); - float refresh = CGDisplayModeGetRefreshRate(displayMode); - CGDisplayModeRelease(displayMode); - if (refresh > 0) - m_refreshRate = refresh; - - // Get m_name (brand/model of the monitor) - NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName); - NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; - if ([localizedNames count] > 0) - m_name = QString::fromUtf8([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]); - [deviceInfo release]; - - QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); - QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second); - QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate); -} - -qreal QCocoaScreen::devicePixelRatio() const -{ - QMacAutoReleasePool pool; - NSScreen *nsScreen = nativeScreen(); - return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0); -} - -QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const -{ - QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); - if (type == QPlatformScreen::Subpixel_None) { - // Every OSX machine has RGB pixels unless a peculiar or rotated non-Apple screen is attached - type = QPlatformScreen::Subpixel_RGB; - } - return type; -} - -QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const -{ - NSPoint screenPoint = qt_mac_flipPoint(point); - - // Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint: - // belowWindowWithWindowNumber] may return windows that are not interesting - // to Qt. The search iterates until a suitable window or no window is found. - NSInteger topWindowNumber = 0; - QWindow *window = 0; - do { - // Get the top-most window, below any previously rejected window. - topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint - belowWindowWithWindowNumber:topWindowNumber]; - - // Continue the search if the window does not belong to this process. - NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber]; - if (nsWindow == 0) - continue; - - // Continue the search if the window does not belong to Qt. - if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) - continue; - - id<QNSWindowProtocol> proto = static_cast<id<QNSWindowProtocol> >(nsWindow); - QCocoaWindow *cocoaWindow = proto.helper.platformWindow; - if (!cocoaWindow) - continue; - window = cocoaWindow->window(); - - // Continue the search if the window is not a top-level window. - if (!window->isTopLevel()) - continue; - - // Stop searching. The current window is the correct window. - break; - } while (topWindowNumber > 0); - - return window; -} - -QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const -{ - // TODO window should be handled - Q_UNUSED(window) - - const int maxDisplays = 128; // 128 displays should be enough for everyone. - CGDirectDisplayID displays[maxDisplays]; - CGDisplayCount displayCount; - CGRect cgRect; - - if (width < 0 || height < 0) { - // get all displays - cgRect = CGRectInfinite; - } else { - cgRect = CGRectMake(x, y, width, height); - } - const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount); - - if (err && displayCount == 0) - return QPixmap(); - - // calculate pixmap size - QSize windowSize(width, height); - if (width < 0 || height < 0) { - QRect windowRect; - for (uint i = 0; i < displayCount; ++i) { - const CGRect cgRect = CGDisplayBounds(displays[i]); - QRect qRect(cgRect.origin.x, cgRect.origin.y, cgRect.size.width, cgRect.size.height); - windowRect = windowRect.united(qRect); - } - if (width < 0) - windowSize.setWidth(windowRect.width()); - if (height < 0) - windowSize.setHeight(windowRect.height()); - } - - QPixmap windowPixmap(windowSize * devicePixelRatio()); - windowPixmap.fill(Qt::transparent); - - for (uint i = 0; i < displayCount; ++i) { - const CGRect bounds = CGDisplayBounds(displays[i]); - int w = (width < 0 ? bounds.size.width : width) * devicePixelRatio(); - int h = (height < 0 ? bounds.size.height : height) * devicePixelRatio(); - QRect displayRect = QRect(x, y, w, h); - displayRect = displayRect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y)); - QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i], - CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height())); - QPixmap pix(w, h); - pix.fill(Qt::transparent); - CGRect rect = CGRectMake(0, 0, w, h); - QMacCGContext ctx(&pix); - qt_mac_drawCGImage(ctx, &rect, image); - - QPainter painter(&windowPixmap); - painter.drawPixmap(0, 0, pix); - } - return windowPixmap; -} - static QCocoaIntegration::Options parseOptions(const QStringList ¶mList) { QCocoaIntegration::Options options; @@ -355,7 +142,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) // Move the application window to front to make it take focus, also when launching // from the terminal. On 10.12+ this call has been moved to applicationDidFinishLauching // to work around issues with loss of focus at startup. - if (QSysInfo::macVersion() < QSysInfo::MV_10_12) { + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSSierra) { // Ignoring other apps is necessary (we must ignore the terminal), but makes // Qt apps play slightly less nice with other apps when lanching from Finder // (See the activateIgnoringOtherApps docs.) @@ -392,6 +179,9 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) QMacInternalPasteboardMime::initializeMimeTypes(); QCocoaMimeTypes::initializeMimeTypes(); QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); + + connect(qGuiApp, &QGuiApplication::focusWindowChanged, + this, &QCocoaIntegration::focusWindowChanged); } QCocoaIntegration::~QCocoaIntegration() @@ -435,6 +225,8 @@ QCocoaIntegration::Options QCocoaIntegration::options() const return mOptions; } +Q_LOGGING_CATEGORY(lcCocoaScreen, "qt.qpa.cocoa.screens"); + /*! \brief Synchronizes the screen list, adds new screens, removes deleted ones */ @@ -474,9 +266,11 @@ void QCocoaIntegration::updateScreens() if (screen) { remainingScreens.remove(screen); screen->updateGeometry(); + qCDebug(lcCocoaScreen) << "Updated properties of" << screen; } else { screen = new QCocoaScreen(i); mScreens.append(screen); + qCDebug(lcCocoaScreen) << "Adding" << screen; screenAdded(screen); } siblings << screen; @@ -493,6 +287,7 @@ void QCocoaIntegration::updateScreens() mScreens.removeOne(screen); // Prevent stale references to NSScreen during destroy screen->m_screenIndex = -1; + qCDebug(lcCocoaScreen) << "Removing" << screen; destroyScreen(screen); } } @@ -545,6 +340,24 @@ QPlatformWindow *QCocoaIntegration::createForeignWindow(QWindow *window, WId nat return new QCocoaWindow(window, nativeHandle); } +class QCocoaOffscreenSurface : public QPlatformOffscreenSurface +{ +public: + QCocoaOffscreenSurface(QOffscreenSurface *offscreenSurface) : QPlatformOffscreenSurface(offscreenSurface) {} + + QSurfaceFormat format() const override + { + Q_ASSERT(offscreenSurface()); + return offscreenSurface()->requestedFormat(); + } + bool isValid() const override { return true; } +}; + +QPlatformOffscreenSurface *QCocoaIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + return new QCocoaOffscreenSurface(surface); +} + #ifndef QT_NO_OPENGL QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { @@ -700,4 +513,33 @@ void QCocoaIntegration::beep() const NSBeep(); } +void QCocoaIntegration::focusWindowChanged(QWindow *focusWindow) +{ + // Don't revert icon just because we lost focus + if (!focusWindow) + return; + + static bool hasDefaultApplicationIcon = [](){ + NSImage *genericApplicationIcon = [[NSWorkspace sharedWorkspace] + iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)]; + NSImage *applicationIcon = [NSImage imageNamed:NSImageNameApplicationIcon]; + + NSRect rect = NSMakeRect(0, 0, 32, 32); + return [applicationIcon CGImageForProposedRect:&rect context:nil hints:nil] + == [genericApplicationIcon CGImageForProposedRect:&rect context:nil hints:nil]; + }(); + + // Don't let the window icon override an explicit application icon set in the Info.plist + if (!hasDefaultApplicationIcon) + return; + + // Or an explicit application icon set on QGuiApplication + if (!qGuiApp->windowIcon().isNull()) + return; + + setApplicationIcon(focusWindow->icon()); +} + +#include "moc_qcocoaintegration.cpp" + QT_END_NAMESPACE |