1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qcocoaintegration.h"
44 #include "qcocoawindow.h"
45 #include "qcocoabackingstore.h"
46 #include "qcocoanativeinterface.h"
47 #include "qcocoamenuloader.h"
48 #include "qcocoaeventdispatcher.h"
49 #include "qcocoahelpers.h"
50 #include "qcocoaapplication.h"
51 #include "qcocoaapplicationdelegate.h"
52 #include "qcocoafiledialoghelper.h"
53 #include "qcocoatheme.h"
54 #include "qcocoainputcontext.h"
57 #include <qpa/qplatformaccessibility.h>
58 #include <QtCore/qcoreapplication.h>
60 #include <QtPlatformSupport/private/qcoretextfontdatabase_p.h>
61 #include <IOKit/graphics/IOGraphicsLib.h>
63 static void initResources()
65 Q_INIT_RESOURCE_EXTERN(qcocoaresources)
66 Q_INIT_RESOURCE(qcocoaresources);
71 QCocoaScreen::QCocoaScreen(int screenIndex) :
72 QPlatformScreen(), m_refreshRate(60.0)
74 m_screen = [[NSScreen screens] objectAtIndex:screenIndex];
76 m_cursor = new QCocoaCursor;
79 QCocoaScreen::~QCocoaScreen()
84 void QCocoaScreen::updateGeometry()
86 NSRect frameRect = [m_screen frame];
87 m_geometry = QRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
88 NSRect visibleRect = [m_screen visibleFrame];
89 m_availableGeometry = QRect(visibleRect.origin.x,
90 frameRect.size.height - (visibleRect.origin.y + visibleRect.size.height), // invert y
91 visibleRect.size.width, visibleRect.size.height);
93 m_format = QImage::Format_RGB32;
94 m_depth = NSBitsPerPixelFromDepth([m_screen depth]);
96 NSDictionary *devDesc = [m_screen deviceDescription];
97 CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue];
98 CGSize size = CGDisplayScreenSize(dpy);
99 m_physicalSize = QSizeF(size.width, size.height);
100 m_logicalDpi.first = 72;
101 m_logicalDpi.second = 72;
102 m_refreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy));
104 // Get m_name (brand/model of the monitor)
105 NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName);
106 NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
107 if ([localizedNames count] > 0)
108 m_name = QString::fromUtf8([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
109 [deviceInfo release];
111 QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry());
112 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
113 QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
114 QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), availableGeometry());
117 extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev);
119 QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const
121 // TODO window should be handled
124 const int maxDisplays = 128; // 128 displays should be enough for everyone.
125 CGDirectDisplayID displays[maxDisplays];
126 CGDisplayCount displayCount;
129 if (width < 0 || height < 0) {
131 cgRect = CGRectInfinite;
133 cgRect = CGRectMake(x, y, width, height);
135 const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
137 if (err && displayCount == 0)
140 // calculate pixmap size
141 QSize windowSize(width, height);
142 if (width < 0 || height < 0) {
144 for (uint i = 0; i < displayCount; ++i) {
145 const CGRect cgRect = CGDisplayBounds(displays[i]);
146 QRect qRect(cgRect.origin.x, cgRect.origin.y, cgRect.size.width, cgRect.size.height);
147 windowRect = windowRect.united(qRect);
150 windowSize.setWidth(windowRect.width());
152 windowSize.setHeight(windowRect.height());
155 QPixmap windowPixmap(windowSize);
156 windowPixmap.fill(Qt::transparent);
158 for (uint i = 0; i < displayCount; ++i) {
159 const CGRect bounds = CGDisplayBounds(displays[i]);
160 int w = (width < 0 ? bounds.size.width : width);
161 int h = (height < 0 ? bounds.size.height : height);
162 QRect displayRect = QRect(x, y, w, h);
163 QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i],
164 CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height()));
166 pix.fill(Qt::transparent);
167 CGRect rect = CGRectMake(0, 0, w, h);
168 CGContextRef ctx = qt_mac_cg_context(&pix);
169 qt_mac_drawCGImage(ctx, &rect, image);
170 CGContextRelease(ctx);
172 QPainter painter(&windowPixmap);
173 painter.drawPixmap(bounds.origin.x, bounds.origin.y, pix);
178 QCocoaIntegration::QCocoaIntegration()
179 : mFontDb(new QCoreTextFontDatabase())
180 , mEventDispatcher(new QCocoaEventDispatcher())
181 , mInputContext(new QCocoaInputContext)
182 #ifndef QT_NO_ACCESSIBILITY
183 , mAccessibility(new QPlatformAccessibility)
185 , mCocoaClipboard(new QCocoaClipboard)
186 , mCocoaDrag(new QCocoaDrag)
187 , mNativeInterface(new QCocoaNativeInterface)
188 , mServices(new QCocoaServices)
191 QCocoaAutoReleasePool pool;
193 qApp->setAttribute(Qt::AA_DontUseNativeMenuBar, false);
195 NSApplication *cocoaApplication = [QT_MANGLE_NAMESPACE(QNSApplication) sharedApplication];
196 qt_redirectNSApplicationSendEvent();
198 if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
199 // Applications launched from plain executables (without an app
200 // bundle) are "background" applications that does not take keybaord
201 // focus or have a dock icon or task switcher entry. Qt Gui apps generally
202 // wants to be foreground applications so change the process type. (But
203 // see the function implementation for exceptions.)
204 qt_mac_transformProccessToForegroundApplication();
206 // Move the application window to front to avoid launching behind the terminal.
207 // Ignoring other apps is neccessary (we must ignore the terminal), but makes
208 // Qt apps play slightly less nice with other apps when lanching from Finder
209 // (See the activateIgnoringOtherApps docs.)
210 [cocoaApplication activateIgnoringOtherApps : YES];
213 // ### For AA_MacPluginApplication we don't want to load the menu nib.
214 // Qt 4 also does not set the application delegate, so that behavior
216 if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
218 // Set app delegate, link to the current delegate (if any)
219 QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
220 [newDelegate setReflectionDelegate:[cocoaApplication delegate]];
221 [cocoaApplication setDelegate:newDelegate];
223 // Load the application menu. This menu contains Preferences, Hide, Quit.
224 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
225 qt_mac_loadMenuNib(qtMenuLoader);
226 [cocoaApplication setMenu:[qtMenuLoader menu]];
227 [newDelegate setMenuLoader:qtMenuLoader];
232 QMacPasteboardMime::initializeMimeTypes();
235 QCocoaIntegration::~QCocoaIntegration()
237 qt_resetNSApplicationSendEvent();
239 QCocoaAutoReleasePool pool;
240 if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
241 // remove the apple event handlers installed by QCocoaApplicationDelegate
242 QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *delegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
243 [delegate removeAppleEventHandlers];
244 // reset the application delegate
245 [[NSApplication sharedApplication] setDelegate: 0];
248 // Delete the clipboard integration and destroy mime type converters.
249 // Deleting the clipboard integration flushes promised pastes using
250 // the mime converters - the ordering here is important.
251 delete mCocoaClipboard;
252 QMacPasteboardMime::destroyMimeTypes();
254 // Delete screens in reverse order to avoid crash in case of multiple screens
255 while (!mScreens.isEmpty()) {
256 delete mScreens.takeLast();
261 \brief Synchronizes the screen list, adds new screens, removes deleted ones
263 void QCocoaIntegration::updateScreens()
265 NSArray *screens = [NSScreen screens];
266 QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
267 QList<QPlatformScreen *> siblings;
268 for (uint i = 0; i < [screens count]; i++) {
269 NSScreen* scr = [[NSScreen screens] objectAtIndex:i];
270 CGDirectDisplayID dpy = [[[scr deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
271 // If this screen is a mirror and is not the primary one of the mirror set, ignore it.
272 if (CGDisplayIsInMirrorSet(dpy)) {
273 CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy);
274 if (primary != kCGNullDirectDisplay && primary != dpy)
277 QCocoaScreen* screen = NULL;
278 foreach (QCocoaScreen* existingScr, mScreens)
279 // NSScreen documentation says do not cache the array returned from [NSScreen screens].
280 // However in practice, we can identify a screen by its pointer: if resolution changes,
281 // the NSScreen object will be the same instance, just with different values.
282 if (existingScr->osScreen() == scr) {
283 screen = existingScr;
287 remainingScreens.remove(screen);
288 screen->updateGeometry();
290 screen = new QCocoaScreen(i);
291 mScreens.append(screen);
296 // Now the leftovers in remainingScreens are no longer current, so we can delete them.
297 foreach (QCocoaScreen* screen, remainingScreens) {
298 mScreens.removeOne(screen);
301 // All screens in mScreens are siblings, because we ignored the mirrors.
302 foreach (QCocoaScreen* screen, mScreens)
303 screen->setVirtualSiblings(siblings);
306 bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
309 case ThreadedPixmaps:
312 case BufferQueueingOpenGL:
314 case MultipleWindows:
317 return QPlatformIntegration::hasCapability(cap);
323 QPlatformWindow *QCocoaIntegration::createPlatformWindow(QWindow *window) const
325 return new QCocoaWindow(window);
328 QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
330 return new QCocoaGLContext(context->format(), context->shareHandle());
333 QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *window) const
335 return new QCocoaBackingStore(window);
338 QAbstractEventDispatcher *QCocoaIntegration::guiThreadEventDispatcher() const
340 return mEventDispatcher;
343 QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const
345 return mFontDb.data();
348 QPlatformNativeInterface *QCocoaIntegration::nativeInterface() const
350 return mNativeInterface.data();
353 QPlatformInputContext *QCocoaIntegration::inputContext() const
355 return mInputContext.data();
358 QPlatformAccessibility *QCocoaIntegration::accessibility() const
360 #ifndef QT_NO_ACCESSIBILITY
361 return mAccessibility.data();
367 QPlatformClipboard *QCocoaIntegration::clipboard() const
369 return mCocoaClipboard;
372 QPlatformDrag *QCocoaIntegration::drag() const
374 return mCocoaDrag.data();
377 QStringList QCocoaIntegration::themeNames() const
379 return QStringList(QLatin1String(QCocoaTheme::name));
382 QPlatformTheme *QCocoaIntegration::createPlatformTheme(const QString &name) const
384 if (name == QLatin1String(QCocoaTheme::name))
385 return new QCocoaTheme;
386 return QPlatformIntegration::createPlatformTheme(name);
389 QPlatformServices *QCocoaIntegration::services() const
391 return mServices.data();
394 QVariant QCocoaIntegration::styleHint(StyleHint hint) const
396 if (hint == QPlatformIntegration::FontSmoothingGamma)
398 if (hint == QPlatformIntegration::SynthesizeMouseFromTouchEvents)
401 return QPlatformIntegration::styleHint(hint);