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 "qiosintegration.h"
41#include "qioseventdispatcher.h"
42#include "qiosglobal.h"
43#include "qioswindow.h"
44#include "qiosbackingstore.h"
45#include "qiosscreen.h"
46#include "qiosplatformaccessibility.h"
47#include "qioscontext.h"
48#ifndef Q_OS_TVOS
49#include "qiosclipboard.h"
50#endif
51#include "qiosinputcontext.h"
52#include "qiostheme.h"
53#include "qiosservices.h"
54#include "qiosoptionalplugininterface.h"
55
56#include <QtGui/private/qguiapplication_p.h>
57
58#include <qoffscreensurface.h>
59#include <qpa/qplatformoffscreensurface.h>
60
61#include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h>
62#include <QtClipboardSupport/private/qmacmime_p.h>
63#include <QDir>
64#include <QOperatingSystemVersion>
65
66#import <AudioToolbox/AudioServices.h>
67
68#include <QtDebug>
69
70QT_BEGIN_NAMESPACE
71
72class QCoreTextFontEngine;
73
74QIOSIntegration *QIOSIntegration::instance()
75{
76    return static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
77}
78
79QIOSIntegration::QIOSIntegration()
80    : m_fontDatabase(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>)
81#if !defined(Q_OS_TVOS) && !defined(QT_NO_CLIPBOARD)
82    , m_clipboard(new QIOSClipboard)
83#endif
84    , m_inputContext(0)
85    , m_platformServices(new QIOSServices)
86    , m_accessibility(0)
87    , m_optionalPlugins(new QFactoryLoader(QIosOptionalPluginInterface_iid, QLatin1String("/platforms/darwin")))
88{
89    if (Q_UNLIKELY(!qt_apple_isApplicationExtension() && !qt_apple_sharedApplication())) {
90        qFatal("Error: You are creating QApplication before calling UIApplicationMain.\n" \
91               "If you are writing a native iOS application, and only want to use Qt for\n" \
92               "parts of the application, a good place to create QApplication is from within\n" \
93               "'applicationDidFinishLaunching' inside your UIApplication delegate.\n");
94    }
95
96    // Set current directory to app bundle folder
97    QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String]));
98}
99
100void QIOSIntegration::initialize()
101{
102    UIScreen *mainScreen = [UIScreen mainScreen];
103    NSMutableArray<UIScreen *> *screens = [[[UIScreen screens] mutableCopy] autorelease];
104    if (![screens containsObject:mainScreen]) {
105        // Fallback for iOS 7.1 (QTBUG-42345)
106        [screens insertObject:mainScreen atIndex:0];
107    }
108
109    for (UIScreen *screen in screens)
110        QWindowSystemInterface::handleScreenAdded(new QIOSScreen(screen));
111
112    // Depends on a primary screen being present
113    m_inputContext = new QIOSInputContext;
114
115    m_touchDevice = new QTouchDevice;
116    m_touchDevice->setType(QTouchDevice::TouchScreen);
117    QTouchDevice::Capabilities touchCapabilities = QTouchDevice::Position | QTouchDevice::NormalizedPosition;
118    if (mainScreen.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
119        touchCapabilities |= QTouchDevice::Pressure;
120    m_touchDevice->setCapabilities(touchCapabilities);
121    QWindowSystemInterface::registerTouchDevice(m_touchDevice);
122#if QT_CONFIG(tabletevent)
123    QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
124#endif
125    QMacInternalPasteboardMime::initializeMimeTypes();
126
127    for (int i = 0; i < m_optionalPlugins->metaData().size(); ++i)
128        qobject_cast<QIosOptionalPluginInterface *>(m_optionalPlugins->instance(i))->initPlugin();
129}
130
131QIOSIntegration::~QIOSIntegration()
132{
133    delete m_fontDatabase;
134    m_fontDatabase = 0;
135
136#if !defined(Q_OS_TVOS) && !defined(QT_NO_CLIPBOARD)
137    delete m_clipboard;
138    m_clipboard = 0;
139#endif
140    QMacInternalPasteboardMime::destroyMimeTypes();
141
142    delete m_inputContext;
143    m_inputContext = 0;
144
145    foreach (QScreen *screen, QGuiApplication::screens())
146        QWindowSystemInterface::handleScreenRemoved(screen->handle());
147
148    delete m_platformServices;
149    m_platformServices = 0;
150
151    delete m_accessibility;
152    m_accessibility = 0;
153
154    delete m_optionalPlugins;
155    m_optionalPlugins = 0;
156}
157
158bool QIOSIntegration::hasCapability(Capability cap) const
159{
160    switch (cap) {
161    case BufferQueueingOpenGL:
162        return true;
163    case OpenGL:
164    case ThreadedOpenGL:
165        return true;
166    case ThreadedPixmaps:
167        return true;
168    case MultipleWindows:
169        return true;
170    case WindowManagement:
171        return false;
172    case ApplicationState:
173        return true;
174    case RasterGLSurface:
175        return true;
176    default:
177        return QPlatformIntegration::hasCapability(cap);
178    }
179}
180
181QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const
182{
183    return new QIOSWindow(window);
184}
185
186// Used when the QWindow's surface type is set by the client to QSurface::RasterSurface
187QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const
188{
189    return new QIOSBackingStore(window);
190}
191
192// Used when the QWindow's surface type is set by the client to QSurface::OpenGLSurface
193QPlatformOpenGLContext *QIOSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
194{
195    return new QIOSContext(context);
196}
197
198class QIOSOffscreenSurface : public QPlatformOffscreenSurface
199{
200public:
201    QIOSOffscreenSurface(QOffscreenSurface *offscreenSurface) : QPlatformOffscreenSurface(offscreenSurface) {}
202
203    QSurfaceFormat format() const override
204    {
205        Q_ASSERT(offscreenSurface());
206        return offscreenSurface()->requestedFormat();
207    }
208    bool isValid() const override { return true; }
209};
210
211QPlatformOffscreenSurface *QIOSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
212{
213    return new QIOSOffscreenSurface(surface);
214}
215
216QAbstractEventDispatcher *QIOSIntegration::createEventDispatcher() const
217{
218    return QIOSEventDispatcher::create();
219}
220
221QPlatformFontDatabase * QIOSIntegration::fontDatabase() const
222{
223    return m_fontDatabase;
224}
225
226#ifndef QT_NO_CLIPBOARD
227QPlatformClipboard *QIOSIntegration::clipboard() const
228{
229#ifndef Q_OS_TVOS
230    return m_clipboard;
231#else
232    return QPlatformIntegration::clipboard();
233#endif
234}
235#endif
236
237QPlatformInputContext *QIOSIntegration::inputContext() const
238{
239    return m_inputContext;
240}
241
242QPlatformServices *QIOSIntegration::services() const
243{
244    return m_platformServices;
245}
246
247QVariant QIOSIntegration::styleHint(StyleHint hint) const
248{
249    switch (hint) {
250    case StartDragTime:
251        return 300;
252    case PasswordMaskDelay:
253        // this number is based on timing the native delay
254        // since there is no API to get it
255        return 2000;
256    case ShowIsMaximized:
257        return true;
258    case SetFocusOnTouchRelease:
259        return true;
260    default:
261        return QPlatformIntegration::styleHint(hint);
262    }
263}
264
265QStringList QIOSIntegration::themeNames() const
266{
267    return QStringList(QLatin1String(QIOSTheme::name));
268}
269
270QPlatformTheme *QIOSIntegration::createPlatformTheme(const QString &name) const
271{
272    if (name == QLatin1String(QIOSTheme::name))
273        return new QIOSTheme;
274
275    return QPlatformIntegration::createPlatformTheme(name);
276}
277
278QTouchDevice *QIOSIntegration::touchDevice()
279{
280    return m_touchDevice;
281}
282
283#ifndef QT_NO_ACCESSIBILITY
284QPlatformAccessibility *QIOSIntegration::accessibility() const
285{
286    if (!m_accessibility)
287        m_accessibility = new QIOSPlatformAccessibility;
288    return m_accessibility;
289}
290#endif
291
292QPlatformNativeInterface *QIOSIntegration::nativeInterface() const
293{
294    return const_cast<QIOSIntegration *>(this);
295}
296
297void QIOSIntegration::beep() const
298{
299#if !TARGET_IPHONE_SIMULATOR
300    AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
301#endif
302}
303
304// ---------------------------------------------------------
305
306void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
307{
308    if (!window || !window->handle())
309        return 0;
310
311    QByteArray lowerCaseResource = resource.toLower();
312
313    QIOSWindow *platformWindow = static_cast<QIOSWindow *>(window->handle());
314
315    if (lowerCaseResource == "uiview")
316        return reinterpret_cast<void *>(platformWindow->winId());
317
318    return 0;
319}
320
321// ---------------------------------------------------------
322
323#include "moc_qiosintegration.cpp"
324
325QT_END_NAMESPACE
326