Implement Cocoa KeyMapper.
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qcocoaintegration.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 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.
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 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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcocoaintegration.h"
43
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"
55 #include "qmacmime.h"
56 #include "qcocoaaccessibility.h"
57
58 #include <qpa/qplatformaccessibility.h>
59 #include <QtCore/qcoreapplication.h>
60
61 #include <QtPlatformSupport/private/qcoretextfontdatabase_p.h>
62 #include <IOKit/graphics/IOGraphicsLib.h>
63
64 static void initResources()
65 {
66     Q_INIT_RESOURCE_EXTERN(qcocoaresources)
67     Q_INIT_RESOURCE(qcocoaresources);
68 }
69
70 QT_BEGIN_NAMESPACE
71
72 QCocoaScreen::QCocoaScreen(int screenIndex) :
73     QPlatformScreen(), m_refreshRate(60.0)
74 {
75     m_screen = [[NSScreen screens] objectAtIndex:screenIndex];
76     updateGeometry();
77     m_cursor = new QCocoaCursor;
78 }
79
80 QCocoaScreen::~QCocoaScreen()
81 {
82     delete m_cursor;
83 }
84
85 void QCocoaScreen::updateGeometry()
86 {
87     NSRect frameRect = [m_screen frame];
88     m_geometry = QRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
89     NSRect visibleRect = [m_screen visibleFrame];
90     m_availableGeometry = QRect(visibleRect.origin.x,
91                                 frameRect.size.height - (visibleRect.origin.y + visibleRect.size.height), // invert y
92                                 visibleRect.size.width, visibleRect.size.height);
93
94     m_format = QImage::Format_RGB32;
95     m_depth = NSBitsPerPixelFromDepth([m_screen depth]);
96
97     NSDictionary *devDesc = [m_screen deviceDescription];
98     CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue];
99     CGSize size = CGDisplayScreenSize(dpy);
100     m_physicalSize = QSizeF(size.width, size.height);
101     m_logicalDpi.first = 72;
102     m_logicalDpi.second = 72;
103     m_refreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy));
104
105     // Get m_name (brand/model of the monitor)
106     NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName);
107     NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
108     if ([localizedNames count] > 0)
109         m_name = QString::fromUtf8([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
110     [deviceInfo release];
111
112     QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry());
113     QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
114     QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
115     QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), availableGeometry());
116 }
117
118 qreal QCocoaScreen::devicePixelRatio() const
119 {
120 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
121     if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
122         return qreal([m_screen backingScaleFactor]);
123     } else
124 #endif
125     {
126         return 1.0;
127     }
128 }
129
130 extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev);
131
132 QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const
133 {
134     // TODO window should be handled
135     Q_UNUSED(window)
136
137     const int maxDisplays = 128; // 128 displays should be enough for everyone.
138     CGDirectDisplayID displays[maxDisplays];
139     CGDisplayCount displayCount;
140     CGRect cgRect;
141
142     if (width < 0 || height < 0) {
143         // get all displays
144         cgRect = CGRectInfinite;
145     } else {
146         cgRect = CGRectMake(x, y, width, height);
147     }
148     const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
149
150     if (err && displayCount == 0)
151         return QPixmap();
152
153     // calculate pixmap size
154     QSize windowSize(width, height);
155     if (width < 0 || height < 0) {
156         QRect windowRect;
157         for (uint i = 0; i < displayCount; ++i) {
158             const CGRect cgRect = CGDisplayBounds(displays[i]);
159             QRect qRect(cgRect.origin.x, cgRect.origin.y, cgRect.size.width, cgRect.size.height);
160             windowRect = windowRect.united(qRect);
161         }
162         if (width < 0)
163             windowSize.setWidth(windowRect.width());
164         if (height < 0)
165             windowSize.setHeight(windowRect.height());
166     }
167
168     QPixmap windowPixmap(windowSize);
169     windowPixmap.fill(Qt::transparent);
170
171     for (uint i = 0; i < displayCount; ++i) {
172         const CGRect bounds = CGDisplayBounds(displays[i]);
173         int w = (width < 0 ? bounds.size.width : width);
174         int h = (height < 0 ? bounds.size.height : height);
175         QRect displayRect = QRect(x, y, w, h);
176         QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i],
177             CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height()));
178         QPixmap pix(w, h);
179         pix.fill(Qt::transparent);
180         CGRect rect = CGRectMake(0, 0, w, h);
181         CGContextRef ctx = qt_mac_cg_context(&pix);
182         qt_mac_drawCGImage(ctx, &rect, image);
183         CGContextRelease(ctx);
184
185         QPainter painter(&windowPixmap);
186         painter.drawPixmap(bounds.origin.x, bounds.origin.y, pix);
187     }
188     return windowPixmap;
189 }
190
191 QCocoaIntegration::QCocoaIntegration()
192     : mFontDb(new QCoreTextFontDatabase())
193     , mEventDispatcher(new QCocoaEventDispatcher())
194     , mInputContext(new QCocoaInputContext)
195 #ifndef QT_NO_COCOA_ACCESSIBILITY
196     , mAccessibility(new QCococaAccessibility)
197 #endif
198     , mCocoaClipboard(new QCocoaClipboard)
199     , mCocoaDrag(new QCocoaDrag)
200     , mNativeInterface(new QCocoaNativeInterface)
201     , mServices(new QCocoaServices)
202     , mKeyboardMapper(new QCocoaKeyMapper)
203 {
204     initResources();
205     QCocoaAutoReleasePool pool;
206
207     qApp->setAttribute(Qt::AA_DontUseNativeMenuBar, false);
208
209     NSApplication *cocoaApplication = [QT_MANGLE_NAMESPACE(QNSApplication) sharedApplication];
210     qt_redirectNSApplicationSendEvent();
211
212     if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
213         // Applications launched from plain executables (without an app
214         // bundle) are "background" applications that does not take keybaord
215         // focus or have a dock icon or task switcher entry. Qt Gui apps generally
216         // wants to be foreground applications so change the process type. (But
217         // see the function implementation for exceptions.)
218         qt_mac_transformProccessToForegroundApplication();
219
220         // Move the application window to front to avoid launching behind the terminal.
221         // Ignoring other apps is neccessary (we must ignore the terminal), but makes
222         // Qt apps play slightly less nice with other apps when lanching from Finder
223         // (See the activateIgnoringOtherApps docs.)
224         [cocoaApplication activateIgnoringOtherApps : YES];
225     }
226
227     // ### For AA_MacPluginApplication we don't want to load the menu nib.
228     // Qt 4 also does not set the application delegate, so that behavior
229     // is matched here.
230     if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
231
232         // Set app delegate, link to the current delegate (if any)
233         QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
234         [newDelegate setReflectionDelegate:[cocoaApplication delegate]];
235         [cocoaApplication setDelegate:newDelegate];
236
237         // Load the application menu. This menu contains Preferences, Hide, Quit.
238         QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
239         qt_mac_loadMenuNib(qtMenuLoader);
240         [cocoaApplication setMenu:[qtMenuLoader menu]];
241         [newDelegate setMenuLoader:qtMenuLoader];
242     }
243
244     updateScreens();
245
246     QMacPasteboardMime::initializeMimeTypes();
247 }
248
249 QCocoaIntegration::~QCocoaIntegration()
250 {
251     qt_resetNSApplicationSendEvent();
252
253     QCocoaAutoReleasePool pool;
254     if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
255         // remove the apple event handlers installed by QCocoaApplicationDelegate
256         QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *delegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
257         [delegate removeAppleEventHandlers];
258         // reset the application delegate
259         [[NSApplication sharedApplication] setDelegate: 0];
260     }
261
262     // Delete the clipboard integration and destroy mime type converters.
263     // Deleting the clipboard integration flushes promised pastes using
264     // the mime converters - the ordering here is important.
265     delete mCocoaClipboard;
266     QMacPasteboardMime::destroyMimeTypes();
267
268     // Delete screens in reverse order to avoid crash in case of multiple screens
269     while (!mScreens.isEmpty()) {
270         delete mScreens.takeLast();
271     }
272 }
273
274 /*!
275     \brief Synchronizes the screen list, adds new screens, removes deleted ones
276 */
277 void QCocoaIntegration::updateScreens()
278 {
279     NSArray *screens = [NSScreen screens];
280     QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
281     QList<QPlatformScreen *> siblings;
282     for (uint i = 0; i < [screens count]; i++) {
283         NSScreen* scr = [[NSScreen screens] objectAtIndex:i];
284         CGDirectDisplayID dpy = [[[scr deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
285         // If this screen is a mirror and is not the primary one of the mirror set, ignore it.
286         if (CGDisplayIsInMirrorSet(dpy)) {
287             CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy);
288             if (primary != kCGNullDirectDisplay && primary != dpy)
289                 continue;
290         }
291         QCocoaScreen* screen = NULL;
292         foreach (QCocoaScreen* existingScr, mScreens)
293             // NSScreen documentation says do not cache the array returned from [NSScreen screens].
294             // However in practice, we can identify a screen by its pointer: if resolution changes,
295             // the NSScreen object will be the same instance, just with different values.
296             if (existingScr->osScreen() == scr) {
297                 screen = existingScr;
298                 break;
299             }
300         if (screen) {
301             remainingScreens.remove(screen);
302             screen->updateGeometry();
303         } else {
304             screen = new QCocoaScreen(i);
305             mScreens.append(screen);
306             screenAdded(screen);
307         }
308         siblings << screen;
309     }
310     // Now the leftovers in remainingScreens are no longer current, so we can delete them.
311     foreach (QCocoaScreen* screen, remainingScreens) {
312         mScreens.removeOne(screen);
313         delete screen;
314     }
315     // All screens in mScreens are siblings, because we ignored the mirrors.
316     foreach (QCocoaScreen* screen, mScreens)
317         screen->setVirtualSiblings(siblings);
318 }
319
320 bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
321 {
322     switch (cap) {
323     case ThreadedPixmaps:
324     case OpenGL:
325     case ThreadedOpenGL:
326     case BufferQueueingOpenGL:
327     case WindowMasks:
328     case MultipleWindows:
329         return true;
330     default:
331         return QPlatformIntegration::hasCapability(cap);
332     }
333 }
334
335
336
337 QPlatformWindow *QCocoaIntegration::createPlatformWindow(QWindow *window) const
338 {
339     return new QCocoaWindow(window);
340 }
341
342 QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
343 {
344     return new QCocoaGLContext(context->format(), context->shareHandle());
345 }
346
347 QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *window) const
348 {
349     return new QCocoaBackingStore(window);
350 }
351
352 QAbstractEventDispatcher *QCocoaIntegration::guiThreadEventDispatcher() const
353 {
354     return mEventDispatcher;
355 }
356
357 QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const
358 {
359     return mFontDb.data();
360 }
361
362 QPlatformNativeInterface *QCocoaIntegration::nativeInterface() const
363 {
364     return mNativeInterface.data();
365 }
366
367 QPlatformInputContext *QCocoaIntegration::inputContext() const
368 {
369     return mInputContext.data();
370 }
371
372 QPlatformAccessibility *QCocoaIntegration::accessibility() const
373 {
374 #ifndef QT_NO_COCOA_ACCESSIBILITY
375     return mAccessibility.data();
376 #else
377     return 0;
378 #endif
379 }
380
381 QPlatformClipboard *QCocoaIntegration::clipboard() const
382 {
383     return mCocoaClipboard;
384 }
385
386 QPlatformDrag *QCocoaIntegration::drag() const
387 {
388     return mCocoaDrag.data();
389 }
390
391 QStringList QCocoaIntegration::themeNames() const
392 {
393     return QStringList(QLatin1String(QCocoaTheme::name));
394 }
395
396 QPlatformTheme *QCocoaIntegration::createPlatformTheme(const QString &name) const
397 {
398     if (name == QLatin1String(QCocoaTheme::name))
399         return new QCocoaTheme;
400     return QPlatformIntegration::createPlatformTheme(name);
401 }
402
403 QPlatformServices *QCocoaIntegration::services() const
404 {
405     return mServices.data();
406 }
407
408 QVariant QCocoaIntegration::styleHint(StyleHint hint) const
409 {
410     if (hint == QPlatformIntegration::FontSmoothingGamma)
411         return 2.0;
412     if (hint == QPlatformIntegration::SynthesizeMouseFromTouchEvents)
413         return false;
414
415     return QPlatformIntegration::styleHint(hint);
416 }
417
418 QList<int> QCocoaIntegration::possibleKeys(const QKeyEvent *event) const
419 {
420     return mKeyboardMapper->possibleKeys(event);
421 }
422
423 QT_END_NAMESPACE