diff options
Diffstat (limited to 'src/plugins/platforms/qnx')
21 files changed, 982 insertions, 219 deletions
diff --git a/src/plugins/platforms/qnx/main.cpp b/src/plugins/platforms/qnx/main.cpp index ea50b12cb3..fb81928625 100644 --- a/src/plugins/platforms/qnx/main.cpp +++ b/src/plugins/platforms/qnx/main.cpp @@ -46,9 +46,8 @@ QT_BEGIN_NAMESPACE QPlatformIntegration *QQnxIntegrationPlugin::create(const QString& system, const QStringList& paramList) { - Q_UNUSED(paramList); if (system.toLower() == QLatin1String("qnx")) - return new QQnxIntegration; + return new QQnxIntegration(paramList); return 0; } diff --git a/src/plugins/platforms/qnx/main.h b/src/plugins/platforms/qnx/main.h index 0b5f6323c4..683b20efd2 100644 --- a/src/plugins/platforms/qnx/main.h +++ b/src/plugins/platforms/qnx/main.h @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE class QQnxIntegrationPlugin : public QPlatformIntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "qnx.json") + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "qnx.json") public: QPlatformIntegration *create(const QString&, const QStringList&); }; diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 398b64640e..7c497b4434 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -11,11 +11,11 @@ QT += platformsupport-private core-private gui-private CONFIG(blackberry) { CONFIG += qqnx_pps - # Unomment this to enable screen event handling - # through a dedicated thread. - # DEFINES += QQNX_SCREENEVENTTHREAD + # Uncomment following line to enable screen event + # handling through a dedicated thread. + # CONFIG += qqnx_screeneventthread } else { - DEFINES += QQNX_SCREENEVENTTHREAD + CONFIG += qqnx_screeneventthread } # Uncomment these to enable debugging output for various aspects of the plugin @@ -40,11 +40,11 @@ CONFIG(blackberry) { #DEFINES += QQNXVIRTUALKEYBOARD_DEBUG #DEFINES += QQNXWINDOW_DEBUG #DEFINES += QQNXCURSOR_DEBUG +#DEFINES += QQNXFILEPICKER_DEBUG SOURCES = main.cpp \ qqnxbuffer.cpp \ - qqnxscreeneventthread.cpp \ qqnxintegration.cpp \ qqnxscreen.cpp \ qqnxwindow.cpp \ @@ -60,7 +60,6 @@ SOURCES = main.cpp \ HEADERS = main.h \ qqnxbuffer.h \ - qqnxscreeneventthread.h \ qqnxkeytranslator.h \ qqnxintegration.h \ qqnxscreen.h \ @@ -75,6 +74,12 @@ HEADERS = main.h \ qqnxservices.h \ qqnxcursor.h +CONFIG(qqnx_screeneventthread) { + DEFINES += QQNX_SCREENEVENTTHREAD + SOURCES += qqnxscreeneventthread.cpp + HEADERS += qqnxscreeneventthread.h +} + LIBS += -lscreen contains(QT_CONFIG, opengles2) { @@ -91,8 +96,7 @@ CONFIG(blackberry) { qqnxbpseventfilter.cpp \ qqnxvirtualkeyboardbps.cpp \ qqnxtheme.cpp \ - qqnxsystemsettings.cpp \ - qqnxfiledialoghelper.cpp + qqnxsystemsettings.cpp HEADERS += qqnxnavigatorbps.h \ qqnxeventdispatcher_blackberry.h \ @@ -105,6 +109,17 @@ CONFIG(blackberry) { LIBS += -lbps } +CONFIG(blackberry-playbook) { + SOURCES += qqnxfiledialoghelper_playbook.cpp +} else { + CONFIG(blackberry) { + SOURCES += qqnxfiledialoghelper_bb10.cpp \ + qqnxfilepicker.cpp + + HEADERS += qqnxfilepicker.h + } +} + CONFIG(qqnx_pps) { DEFINES += QQNX_PPS diff --git a/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp b/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp index e723e32301..4c36a97ab6 100644 --- a/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp +++ b/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp @@ -41,11 +41,12 @@ #include "qqnxbpseventfilter.h" #include "qqnxnavigatoreventhandler.h" -#include "qqnxfiledialoghelper.h" #include "qqnxscreen.h" #include "qqnxscreeneventhandler.h" #include "qqnxvirtualkeyboardbps.h" +#include "qqnxfiledialoghelper.h" +#include <QCoreApplication> #include <QAbstractEventDispatcher> #include <QDebug> @@ -126,6 +127,7 @@ void QQnxBpsEventFilter::unregisterForScreenEvents(QQnxScreen *screen) qWarning("QQNX: failed to unregister for screen events on screen %p", screen->nativeContext()); } +#if defined(Q_OS_BLACKBERRY_TABLET) void QQnxBpsEventFilter::registerForDialogEvents(QQnxFileDialogHelper *dialog) { if (dialog_request_events(0) != BPS_SUCCESS) @@ -141,6 +143,7 @@ void QQnxBpsEventFilter::unregisterForDialogEvents(QQnxFileDialogHelper *dialog) if (count == 0) qWarning("QQNX: attempting to unregister dialog that was not registered"); } +#endif // Q_OS_BLACKBERRY_TABLET bool QQnxBpsEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) { @@ -160,12 +163,14 @@ bool QQnxBpsEventFilter::nativeEventFilter(const QByteArray &eventType, void *me return m_screenEventHandler->handleEvent(screenEvent); } +#if defined(Q_OS_BLACKBERRY_TABLET) if (eventDomain == dialog_get_domain()) { dialog_instance_t nativeDialog = dialog_event_get_dialog_instance(event); QQnxFileDialogHelper *dialog = m_dialogMapper.value(nativeDialog, 0); if (dialog) return dialog->handleEvent(event); } +#endif if (eventDomain == navigator_get_domain()) return handleNavigatorEvent(event); @@ -249,6 +254,11 @@ bool QQnxBpsEventFilter::handleNavigatorEvent(bps_event_t *event) break; } + case NAVIGATOR_LOW_MEMORY: + qWarning() << "QGuiApplication based process" << QCoreApplication::applicationPid() + << "received \"NAVIGATOR_LOW_MEMORY\" event"; + return false; + default: qBpsEventFilterDebug() << Q_FUNC_INFO << "Unhandled navigator event. code=" << bps_event_get_code(event); return false; diff --git a/src/plugins/platforms/qnx/qqnxbpseventfilter.h b/src/plugins/platforms/qnx/qqnxbpseventfilter.h index e897863efb..f8e36823d5 100644 --- a/src/plugins/platforms/qnx/qqnxbpseventfilter.h +++ b/src/plugins/platforms/qnx/qqnxbpseventfilter.h @@ -73,8 +73,10 @@ public: void registerForScreenEvents(QQnxScreen *screen); void unregisterForScreenEvents(QQnxScreen *screen); +#ifdef Q_OS_BLACKBERRY_TABLET void registerForDialogEvents(QQnxFileDialogHelper *dialog); void unregisterForDialogEvents(QQnxFileDialogHelper *dialog); +#endif private: bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/qnx/qqnxfiledialoghelper.h b/src/plugins/platforms/qnx/qqnxfiledialoghelper.h index e17ea80501..e7c68f6ff5 100644 --- a/src/plugins/platforms/qnx/qqnxfiledialoghelper.h +++ b/src/plugins/platforms/qnx/qqnxfiledialoghelper.h @@ -44,12 +44,19 @@ #include <qpa/qplatformdialoghelper.h> -#include <bps/dialog.h> QT_BEGIN_NAMESPACE class QQnxIntegration; +#if defined(Q_OS_BLACKBERRY_TABLET) +#include <bps/dialog.h> +#define NativeDialogPtr dialog_instance_t +#else +class QQnxFilePicker; +#define NativeDialogPtr QQnxFilePicker * +#endif + class QQnxFileDialogHelper : public QPlatformFileDialogHelper { Q_OBJECT @@ -57,7 +64,9 @@ public: explicit QQnxFileDialogHelper(const QQnxIntegration *); ~QQnxFileDialogHelper(); +#if defined(Q_OS_BLACKBERRY_TABLET) bool handleEvent(bps_event_t *event); +#endif void exec(); @@ -65,29 +74,32 @@ public: void hide(); bool defaultNameFilterDisables() const; - void setDirectory(const QString &directory); - QString directory() const; - void selectFile(const QString &fileName); - QStringList selectedFiles() const; + void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; + QUrl directory() const Q_DECL_OVERRIDE; + void selectFile(const QUrl &fileName) Q_DECL_OVERRIDE; + QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE; void setFilter(); void selectNameFilter(const QString &filter); QString selectedNameFilter() const; - dialog_instance_t nativeDialog() const { return m_dialog; } + NativeDialogPtr nativeDialog() const { return m_dialog; } Q_SIGNALS: void dialogClosed(); private: void setNameFilter(const QString &filter); + void setNameFilters(const QStringList &filters); const QQnxIntegration *m_integration; - dialog_instance_t m_dialog; + NativeDialogPtr m_dialog; QFileDialogOptions::AcceptMode m_acceptMode; QString m_selectedFilter; QPlatformDialogHelper::DialogCode m_result; - QStringList m_paths; +#if defined(Q_OS_BLACKBERRY_TABLET) + QList<QUrl> m_paths; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxfiledialoghelper_bb10.cpp b/src/plugins/platforms/qnx/qqnxfiledialoghelper_bb10.cpp new file mode 100644 index 0000000000..dc841eb1a9 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxfiledialoghelper_bb10.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqnxfiledialoghelper.h" + +#include "qqnxfilepicker.h" +#include "qqnxbpseventfilter.h" +#include "qqnxscreen.h" +#include "qqnxintegration.h" + +#include <QDebug> +#include <QEventLoop> +#include <QScreen> +#include <QTimer> +#include <QWindow> + +#ifdef QQNXFILEDIALOGHELPER_DEBUG +#define qFileDialogHelperDebug qDebug +#else +#define qFileDialogHelperDebug QT_NO_QDEBUG_MACRO +#endif + +QT_BEGIN_NAMESPACE + +QQnxFileDialogHelper::QQnxFileDialogHelper(const QQnxIntegration *integration) + : QPlatformFileDialogHelper(), + m_integration(integration), + m_dialog(new QQnxFilePicker), + m_acceptMode(QFileDialogOptions::AcceptOpen), + m_selectedFilter(), + m_result(QPlatformDialogHelper::Rejected) +{ +} + +QQnxFileDialogHelper::~QQnxFileDialogHelper() +{ + delete m_dialog; +} + +void QQnxFileDialogHelper::exec() +{ + qFileDialogHelperDebug() << Q_FUNC_INFO; + + // Clear any previous results + m_dialog->setDirectories(QStringList()); + + QEventLoop loop; + connect(m_dialog, SIGNAL(closed()), &loop, SLOT(quit())); + loop.exec(); + + if (m_dialog->selectedFiles().isEmpty()) + Q_EMIT reject(); + else + Q_EMIT accept(); +} + +bool QQnxFileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + Q_UNUSED(flags); + Q_UNUSED(parent); + Q_UNUSED(modality); + + qFileDialogHelperDebug() << Q_FUNC_INFO; + + // Create dialog + const QSharedPointer<QFileDialogOptions> &opts = options(); + if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) { + // Select one or many files? + const QQnxFilePicker::Mode mode = (opts->fileMode() == QFileDialogOptions::ExistingFiles) + ? QQnxFilePicker::PickerMultiple : QQnxFilePicker::Picker; + + m_dialog->setMode(mode); + + // Set the actual list of extensions + if (!opts->nameFilters().isEmpty()) + setNameFilters(opts->nameFilters()); + else + setNameFilter(tr("All files (*.*)")); + } else { + const QQnxFilePicker::Mode mode = (opts->initiallySelectedFiles().count() >= 2) + ? QQnxFilePicker::SaverMultiple : QQnxFilePicker::Saver; + + m_dialog->setMode(mode); + + if (!opts->initiallySelectedFiles().isEmpty()) { + QStringList files; + Q_FOREACH ( const QUrl &url, opts->initiallySelectedFiles() ) + files.append(url.toLocalFile()); + m_dialog->setDefaultSaveFileNames(files); + } + } + + // Cache the accept mode so we know which functions to use to get the results back + m_acceptMode = opts->acceptMode(); + m_dialog->setTitle(opts->windowTitle()); + m_dialog->open(); + + return true; +} + +void QQnxFileDialogHelper::hide() +{ + qFileDialogHelperDebug() << Q_FUNC_INFO; + m_dialog->close(); +} + +bool QQnxFileDialogHelper::defaultNameFilterDisables() const +{ + qFileDialogHelperDebug() << Q_FUNC_INFO; + return false; +} + +void QQnxFileDialogHelper::setDirectory(const QUrl &directory) +{ + m_dialog->addDirectory(directory.toLocalFile()); +} + +QUrl QQnxFileDialogHelper::directory() const +{ + qFileDialogHelperDebug() << Q_FUNC_INFO; + if (!m_dialog->directories().isEmpty()) + return QUrl::fromLocalFile(m_dialog->directories().first()); + + return QUrl(); +} + +void QQnxFileDialogHelper::selectFile(const QUrl &fileName) +{ + m_dialog->addDefaultSaveFileName(fileName.toLocalFile()); +} + +QList<QUrl> QQnxFileDialogHelper::selectedFiles() const +{ + qFileDialogHelperDebug() << Q_FUNC_INFO; + QList<QUrl> urls; + QStringList files = m_dialog->selectedFiles(); + Q_FOREACH (const QString &file, files) + urls.append(QUrl::fromLocalFile(file)); + return urls; +} + +void QQnxFileDialogHelper::setFilter() +{ + // No native api to support setting a filter from QDir::Filters + qFileDialogHelperDebug() << Q_FUNC_INFO; +} + +void QQnxFileDialogHelper::selectNameFilter(const QString &filter) +{ + qFileDialogHelperDebug() << Q_FUNC_INFO << "filter =" << filter; + setNameFilter(filter); +} + +QString QQnxFileDialogHelper::selectedNameFilter() const +{ + // For now there is no way for the user to change the selected filter + // so this just reflects what the developer has set programmatically. + qFileDialogHelperDebug() << Q_FUNC_INFO; + return m_selectedFilter; +} + +void QQnxFileDialogHelper::setNameFilter(const QString &filter) +{ + qFileDialogHelperDebug() << Q_FUNC_INFO << "filter =" << filter; + + setNameFilters(QPlatformFileDialogHelper::cleanFilterList(filter)); +} + +void QQnxFileDialogHelper::setNameFilters(const QStringList &filters) +{ + qFileDialogHelperDebug() << Q_FUNC_INFO << "filters =" << filters; + + Q_ASSERT(!filters.isEmpty()); + + m_dialog->setFilters(filters); + m_selectedFilter = filters.first(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxfiledialoghelper.cpp b/src/plugins/platforms/qnx/qqnxfiledialoghelper_playbook.cpp index 0325a33268..2a743d03f7 100644 --- a/src/plugins/platforms/qnx/qqnxfiledialoghelper.cpp +++ b/src/plugins/platforms/qnx/qqnxfiledialoghelper_playbook.cpp @@ -102,7 +102,7 @@ bool QQnxFileDialogHelper::handleEvent(bps_event_t *event) for (int i = 0; i < pathCount; ++i) { QString path = QFile::decodeName(filePaths[i]); - m_paths.append(path); + m_paths.append(QUrl::fromLocalFile(path)); qFileDialogHelperDebug() << "path =" << path; } @@ -112,13 +112,13 @@ bool QQnxFileDialogHelper::handleEvent(bps_event_t *event) const char *filePath = dialog_event_get_filesave_filepath(event); QString path = QFile::decodeName(filePath); qFileDialogHelperDebug() << "path =" << path; - m_paths.append(path); + m_paths.append(QUrl::fromLocalFile(path)); } } else { // Cancel m_result = QPlatformDialogHelper::Rejected; } - emit dialogClosed(); + Q_EMIT dialogClosed(); return true; } @@ -135,9 +135,9 @@ void QQnxFileDialogHelper::exec() loop.exec(); if (m_result == QPlatformDialogHelper::Accepted) - emit accept(); + Q_EMIT accept(); else - emit reject(); + Q_EMIT reject(); } bool QQnxFileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) @@ -188,7 +188,7 @@ bool QQnxFileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modali // Maybe pre-select a filename if (!opts->initiallySelectedFiles().isEmpty()) { - QString fileName = opts->initiallySelectedFiles().first(); + QString fileName = opts->initiallySelectedFiles().first().toLocalFile(); dialog_set_filesave_filename(m_dialog, QFile::encodeName(fileName).constData()); } @@ -240,29 +240,29 @@ bool QQnxFileDialogHelper::defaultNameFilterDisables() const return false; } -void QQnxFileDialogHelper::setDirectory(const QString &directory) +void QQnxFileDialogHelper::setDirectory(const QUrl &directory) { qFileDialogHelperDebug() << Q_FUNC_INFO << "directory =" << directory; // No native API for setting the directory(!). The best we can do is to // set it as the file name but even then only with a file save dialog. if (m_dialog && m_acceptMode == QFileDialogOptions::AcceptSave) - dialog_set_filesave_filename(m_dialog, QFile::encodeName(directory).constData()); + dialog_set_filesave_filename(m_dialog, QFile::encodeName(directory.toLocalFile()).constData()); } -QString QQnxFileDialogHelper::directory() const +QUrl QQnxFileDialogHelper::directory() const { qFileDialogHelperDebug() << Q_FUNC_INFO; return m_paths.first(); } -void QQnxFileDialogHelper::selectFile(const QString &fileName) +void QQnxFileDialogHelper::selectFile(const QUrl &fileName) { qFileDialogHelperDebug() << Q_FUNC_INFO << "filename =" << fileName; if (m_dialog && m_acceptMode == QFileDialogOptions::AcceptSave) - dialog_set_filesave_filename(m_dialog, QFile::encodeName(fileName).constData()); + dialog_set_filesave_filename(m_dialog, QFile::encodeName(fileName.toLocalFile()).constData()); } -QStringList QQnxFileDialogHelper::selectedFiles() const +QList<QUrl> QQnxFileDialogHelper::selectedFiles() const { qFileDialogHelperDebug() << Q_FUNC_INFO; return m_paths; @@ -291,9 +291,15 @@ QString QQnxFileDialogHelper::selectedNameFilter() const void QQnxFileDialogHelper::setNameFilter(const QString &filter) { qFileDialogHelperDebug() << Q_FUNC_INFO << "filter =" << filter; + setNameFilters(QPlatformFileDialogHelper::cleanFilterList(filter)); +} + +void QQnxFileDialogHelper::setNameFilters(const QStringList &filters) +{ + qFileDialogHelperDebug() << Q_FUNC_INFO << "filters =" << filters; + + Q_ASSERT(!filters.isEmpty()); - // Extract the globbing expressions - QStringList filters = QPlatformFileDialogHelper::cleanFilterList(filter); char **globs = new char*[filters.size()]; for (int i = 0; i < filters.size(); ++i) { QByteArray glob = filters.at(i).toLocal8Bit(); @@ -303,7 +309,7 @@ void QQnxFileDialogHelper::setNameFilter(const QString &filter) // Set the filters dialog_set_filebrowse_filter(m_dialog, const_cast<const char**>(globs), filters.size()); - m_selectedFilter = filter; + m_selectedFilter = filters.first(); // Cleanup for (int i = 0; i < filters.size(); ++i) diff --git a/src/plugins/platforms/qnx/qqnxfilepicker.cpp b/src/plugins/platforms/qnx/qqnxfilepicker.cpp new file mode 100644 index 0000000000..5229d1f1f5 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxfilepicker.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqnxfilepicker.h" + +#include <QAbstractEventDispatcher> +#include <QCoreApplication> +#include <QDebug> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonParseError> +#include <QUrl> + +#include <bps/navigator.h> +#include <bps/navigator_invoke.h> + +#include <errno.h> + +#ifdef QQNXFILEPICKER_DEBUG +#define qFilePickerDebug qDebug +#else +#define qFilePickerDebug QT_NO_QDEBUG_MACRO +#endif + +static const char s_filePickerTarget[] = "sys.filepicker.target"; + +QQnxFilePicker::QQnxFilePicker(QObject *parent) + : QObject(parent) + , m_invocationHandle(0) + , m_mode(QQnxFilePicker::Picker) + , m_title(tr("Pick a file")) +{ + QCoreApplication::eventDispatcher()->installNativeEventFilter(this); +} + +QQnxFilePicker::~QQnxFilePicker() +{ + cleanup(); + + QCoreApplication::eventDispatcher()->removeNativeEventFilter(this); +} + +void QQnxFilePicker::open() +{ + if (m_invocationHandle) + return; + + int errorCode = BPS_SUCCESS; + + errorCode = navigator_invoke_invocation_create(&m_invocationHandle); + if (errorCode != BPS_SUCCESS) { + qWarning() << "QQnxFilePicker: unable to create invocation:" << strerror(errno); + return; + } + + errorCode = navigator_invoke_invocation_set_target(m_invocationHandle, s_filePickerTarget); + + if (errorCode != BPS_SUCCESS) { + cleanup(); + qWarning() << "QQnxFilePicker: unable to set target:" << strerror(errno); + return; + } + + errorCode = navigator_invoke_invocation_set_action(m_invocationHandle, "bb.action.OPEN"); + if (errorCode != BPS_SUCCESS) { + cleanup(); + qWarning() << "QQnxFilePicker: unable to set action:" << strerror(errno); + return; + } + + errorCode = navigator_invoke_invocation_set_type(m_invocationHandle, "application/vnd.blackberry.file_picker"); + if (errorCode != BPS_SUCCESS) { + cleanup(); + qWarning() << "QQnxFilePicker: unable to set mime type:" << strerror(errno); + return; + } + + QVariantMap map; + map[QStringLiteral("Type")] = QStringLiteral("Other"); + map[QStringLiteral("Mode")] = modeToString(m_mode); + map[QStringLiteral("Title")] = m_title; + map[QStringLiteral("ViewMode")] = QStringLiteral("Default"); + map[QStringLiteral("SortBy")] = QStringLiteral("Default"); + map[QStringLiteral("SortOrder")] = QStringLiteral("Default"); + map[QStringLiteral("ImageCrop")] = false; + map[QStringLiteral("AllowOverwrite")] = false; + + if (!m_defaultSaveFileNames.isEmpty()) + map[QStringLiteral("DefaultFileNames")] = m_defaultSaveFileNames.join(","); + if (!m_filters.isEmpty()) + map[QStringLiteral("Filter")] = m_filters.join(";"); + + QJsonDocument document; + document.setObject(QJsonObject::fromVariantMap(map)); + const QByteArray jsonData = document.toJson(QJsonDocument::Compact); + + errorCode = navigator_invoke_invocation_set_data(m_invocationHandle, jsonData.constData(), jsonData.size()); + if (errorCode != BPS_SUCCESS) { + cleanup(); + qWarning() << "QQnxFilePicker: unable to set data:" << strerror(errno); + return; + } + + navigator_invoke_invocation_send(m_invocationHandle); +} + +void QQnxFilePicker::close() +{ + navigator_card_close_child(); + cleanup(); +} + +bool QQnxFilePicker::nativeEventFilter(const QByteArray&, void *message, long*) +{ + bps_event_t * const event = static_cast<bps_event_t*>(message); + if (!event) + return false; + + if (bps_event_get_code(event) == NAVIGATOR_INVOKE_TARGET_RESULT) { + const char *id = navigator_event_get_id(event); + const char *err = navigator_event_get_err(event); + qFilePickerDebug("received invocation response: id=%s err=%s", id, err); + } else if (bps_event_get_code(event) == NAVIGATOR_CHILD_CARD_CLOSED) { + const char *data = navigator_event_get_card_closed_data(event); + qFilePickerDebug("received data: data='%s'", data); + handleFilePickerResponse(data); + } + + return false; // do not drop the event +} + +void QQnxFilePicker::setMode(QQnxFilePicker::Mode mode) +{ + m_mode = mode; +} + +void QQnxFilePicker::setDefaultSaveFileNames(const QStringList &fileNames) +{ + m_defaultSaveFileNames = fileNames; +} + +void QQnxFilePicker::addDefaultSaveFileName(const QString &fileName) +{ + m_defaultSaveFileNames.append(fileName); +} + +void QQnxFilePicker::setDirectories(const QStringList &directories) +{ + m_directories = directories; +} + +void QQnxFilePicker::addDirectory(const QString &directory) +{ + m_directories.append(directory); +} + +void QQnxFilePicker::setFilters(const QStringList &filters) +{ + m_filters = filters; +} + +void QQnxFilePicker::setTitle(const QString &title) +{ + m_title = title; +} + +QQnxFilePicker::Mode QQnxFilePicker::mode() const +{ + return m_mode; +} + +QStringList QQnxFilePicker::defaultSaveFileNames() const +{ + return m_defaultSaveFileNames; +} + +QStringList QQnxFilePicker::directories() const +{ + return m_directories; +} + +QStringList QQnxFilePicker::filters() const +{ + return m_filters; +} + +QStringList QQnxFilePicker::selectedFiles() const +{ + return m_selectedFiles; +} + +QString QQnxFilePicker::title() const +{ + return m_title; +} + +void QQnxFilePicker::cleanup() +{ + if (m_invocationHandle) { + navigator_invoke_invocation_destroy(m_invocationHandle); + m_invocationHandle = 0; + } +} + +void QQnxFilePicker::handleFilePickerResponse(const char *data) +{ + QJsonParseError jsonError; + QJsonDocument document = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) { + qFilePickerDebug() << "Error parsing FilePicker response: " + << jsonError.errorString(); + Q_EMIT closed(); + cleanup(); + return; + } + + // The response is a list of Json objects. + const QVariantList array = document.array().toVariantList(); + + foreach (const QVariant &variant, array) { + const QJsonObject object = QJsonObject::fromVariantMap(variant.toMap()); + const QUrl url(object.value(QStringLiteral("uri")).toString()); + const QString localFile = url.toLocalFile(); // strip "file://" + + if (!localFile.isEmpty()) + m_selectedFiles << localFile; + + qFilePickerDebug() << "FilePicker uri response:" << localFile; + } + + Q_EMIT closed(); + cleanup(); +} + +QString QQnxFilePicker::modeToString(QQnxFilePicker::Mode mode) const +{ + switch (mode) { + case Picker: + return QStringLiteral("Picker"); + case Saver: + return QStringLiteral("Saver"); + case PickerMultiple: + return QStringLiteral("PickerMultiple"); + case SaverMultiple: + return QStringLiteral("SaverMultiple"); + } + + return QStringLiteral("Picker"); +} diff --git a/src/plugins/platforms/qnx/qqnxfilepicker.h b/src/plugins/platforms/qnx/qqnxfilepicker.h new file mode 100644 index 0000000000..5bb8f0969f --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxfilepicker.h @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** Copyright (C) 2013 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXFILEPICKER_H +#define QQNXFILEPICKER_H + +#include <QAbstractNativeEventFilter> +#include <QObject> +#include <QStringList> + +struct navigator_invoke_invocation_t; + +class QQnxFilePicker : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT + +public: + explicit QQnxFilePicker(QObject *parent = 0); + ~QQnxFilePicker(); + + enum Mode { + Picker, + Saver, + PickerMultiple, + SaverMultiple + }; + + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE; + + void setMode(Mode mode); + void setDefaultSaveFileNames(const QStringList &fileNames); + void addDefaultSaveFileName(const QString &fileName); + void setDirectories(const QStringList &directories); + void addDirectory(const QString &directory); + void setFilters(const QStringList &filters); + void setTitle(const QString &title); + + Mode mode() const; + + QStringList defaultSaveFileNames() const; + QStringList directories() const; + QStringList filters() const; + QStringList selectedFiles() const; + + QString title() const; + +Q_SIGNALS: + void closed(); + +public Q_SLOTS: + void open(); + void close(); + +private: + void cleanup(); + void handleFilePickerResponse(const char *data); + + QString modeToString(Mode mode) const; + + navigator_invoke_invocation_t *m_invocationHandle; + + Mode m_mode; + + QStringList m_defaultSaveFileNames; + QStringList m_directories; + QStringList m_filters; + QStringList m_selectedFiles; + + QString m_title; +}; + +#endif // QQNXFILEPICKER_H diff --git a/src/plugins/platforms/qnx/qqnxglcontext.cpp b/src/plugins/platforms/qnx/qqnxglcontext.cpp index ed959467ff..448509c69d 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.cpp +++ b/src/plugins/platforms/qnx/qqnxglcontext.cpp @@ -60,36 +60,10 @@ QT_BEGIN_NAMESPACE EGLDisplay QQnxGLContext::ms_eglDisplay = EGL_NO_DISPLAY; -static EGLenum checkEGLError(const char *msg) -{ - static const char *errmsg[] = - { - "EGL function succeeded", - "EGL is not initialized, or could not be initialized, for the specified display", - "EGL cannot access a requested resource", - "EGL failed to allocate resources for the requested operation", - "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list", - "EGLConfig argument does not name a valid EGLConfig", - "EGLContext argument does not name a valid EGLContext", - "EGL current surface of the calling thread is no longer valid", - "EGLDisplay argument does not name a valid EGLDisplay", - "EGL arguments are inconsistent", - "EGLNativePixmapType argument does not refer to a valid native pixmap", - "EGLNativeWindowType argument does not refer to a valid native window", - "EGL one or more argument values are invalid", - "EGLSurface argument does not name a valid surface configured for rendering", - "EGL power management event has occurred", - }; - EGLenum error = eglGetError(); - fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]); - return error; -} - QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext) : QPlatformOpenGLContext(), m_glContext(glContext), - m_eglSurface(EGL_NO_SURFACE), - m_newSurfaceRequested(true) // Create a surface the first time makeCurrent() is called + m_currentEglSurface(EGL_NO_SURFACE) { qGLContextDebug() << Q_FUNC_INFO; QSurfaceFormat format = m_glContext->format(); @@ -168,9 +142,31 @@ QQnxGLContext::~QQnxGLContext() // Cleanup EGL context if it exists if (m_eglContext != EGL_NO_CONTEXT) eglDestroyContext(ms_eglDisplay, m_eglContext); +} - // Cleanup EGL surface if it exists - destroySurface(); +EGLenum QQnxGLContext::checkEGLError(const char *msg) +{ + static const char *errmsg[] = + { + "EGL function succeeded", + "EGL is not initialized, or could not be initialized, for the specified display", + "EGL cannot access a requested resource", + "EGL failed to allocate resources for the requested operation", + "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list", + "EGLConfig argument does not name a valid EGLConfig", + "EGLContext argument does not name a valid EGLContext", + "EGL current surface of the calling thread is no longer valid", + "EGLDisplay argument does not name a valid EGLDisplay", + "EGL arguments are inconsistent", + "EGLNativePixmapType argument does not refer to a valid native pixmap", + "EGLNativeWindowType argument does not refer to a valid native window", + "EGL one or more argument values are invalid", + "EGLSurface argument does not name a valid surface configured for rendering", + "EGL power management event has occurred", + }; + EGLenum error = eglGetError(); + fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]); + return error; } void QQnxGLContext::initialize() @@ -199,12 +195,6 @@ void QQnxGLContext::shutdown() eglTerminate(ms_eglDisplay); } -void QQnxGLContext::requestSurfaceChange() -{ - qGLContextDebug() << Q_FUNC_INFO; - m_newSurfaceRequested.testAndSetRelease(false, true); -} - bool QQnxGLContext::makeCurrent(QPlatformSurface *surface) { qGLContextDebug() << Q_FUNC_INFO; @@ -216,14 +206,18 @@ bool QQnxGLContext::makeCurrent(QPlatformSurface *surface) if (eglResult != EGL_TRUE) qFatal("QQnxGLContext: failed to set EGL API, err=%d", eglGetError()); - if (m_newSurfaceRequested.testAndSetOrdered(true, false)) { - qGLContextDebug() << "New EGL surface requested"; + QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface); + if (!platformWindow) + return false; + + platformWindow->setPlatformOpenGLContext(this); + + if (m_currentEglSurface == EGL_NO_SURFACE || m_currentEglSurface != platformWindow->getSurface()) { + m_currentEglSurface = platformWindow->getSurface(); doneCurrent(); - destroySurface(); - createSurface(surface); } - eglResult = eglMakeCurrent(ms_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + eglResult = eglMakeCurrent(ms_eglDisplay, m_currentEglSurface, m_currentEglSurface, m_eglContext); if (eglResult != EGL_TRUE) { checkEGLError("eglMakeCurrent"); qFatal("QQNX: failed to set current EGL context, err=%d", eglGetError()); @@ -248,18 +242,12 @@ void QQnxGLContext::doneCurrent() void QQnxGLContext::swapBuffers(QPlatformSurface *surface) { - Q_UNUSED(surface); qGLContextDebug() << Q_FUNC_INFO; + QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface); + if (!platformWindow) + return; - // Set current rendering API - EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) - qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); - - // Post EGL surface to window - eglResult = eglSwapBuffers(ms_eglDisplay, m_eglSurface); - if (eglResult != EGL_TRUE) - qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError()); + platformWindow->swapEGLBuffers(); } QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName) @@ -275,6 +263,10 @@ QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName) return static_cast<QFunctionPointer>(eglGetProcAddress(procName.constData())); } +EGLDisplay QQnxGLContext::getEglDisplay() { + return ms_eglDisplay; +} + EGLint *QQnxGLContext::contextAttrs() { qGLContextDebug() << Q_FUNC_INFO; @@ -288,66 +280,4 @@ EGLint *QQnxGLContext::contextAttrs() #endif } -bool QQnxGLContext::isCurrent() const -{ - qGLContextDebug() << Q_FUNC_INFO; - return (eglGetCurrentContext() == m_eglContext); -} - -void QQnxGLContext::createSurface(QPlatformSurface *surface) -{ - qGLContextDebug() << Q_FUNC_INFO; - - // Get a pointer to the corresponding platform window - QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface); - if (!platformWindow) - qFatal("QQNX: unable to create EGLSurface without a QQnxWindow"); - - // Link the window and context - platformWindow->setPlatformOpenGLContext(this); - - // Fetch the surface size from the window and update - // the window's buffers before we create the EGL surface - const QSize surfaceSize = platformWindow->requestedBufferSize(); - if (!surfaceSize.isValid()) { - qFatal("QQNX: Trying to create 0 size EGL surface. " - "Please set a valid window size before calling QOpenGLContext::makeCurrent()"); - } - platformWindow->setBufferSize(surfaceSize); - - // Post root window, in case it hasn't been posted yet, to make it appear. - platformWindow->screen()->onWindowPost(platformWindow); - - // Obtain the native handle for our window - screen_window_t handle = platformWindow->nativeHandle(); - - const EGLint eglSurfaceAttrs[] = - { - EGL_RENDER_BUFFER, EGL_BACK_BUFFER, - EGL_NONE - }; - - // Create EGL surface - m_eglSurface = eglCreateWindowSurface(ms_eglDisplay, m_eglConfig, (EGLNativeWindowType) handle, eglSurfaceAttrs); - if (m_eglSurface == EGL_NO_SURFACE) { - checkEGLError("eglCreateWindowSurface"); - qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError()); - } -} - -void QQnxGLContext::destroySurface() -{ - qGLContextDebug() << Q_FUNC_INFO; - - // Destroy EGL surface if it exists - if (m_eglSurface != EGL_NO_SURFACE) { - EGLBoolean eglResult = eglDestroySurface(ms_eglDisplay, m_eglSurface); - if (eglResult != EGL_TRUE) { - qFatal("QQNX: failed to destroy EGL surface, err=%d", eglGetError()); - } - } - - m_eglSurface = EGL_NO_SURFACE; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxglcontext.h b/src/plugins/platforms/qnx/qqnxglcontext.h index 6a7fca7df2..ff57861498 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.h +++ b/src/plugins/platforms/qnx/qqnxglcontext.h @@ -59,6 +59,8 @@ public: QQnxGLContext(QOpenGLContext *glContext); virtual ~QQnxGLContext(); + static EGLenum checkEGLError(const char *msg); + static void initialize(); static void shutdown(); @@ -71,13 +73,10 @@ public: virtual QSurfaceFormat format() const { return m_windowFormat; } - bool isCurrent() const; - - void createSurface(QPlatformSurface *surface); - void destroySurface(); - + static EGLDisplay getEglDisplay(); + EGLConfig getEglConfig() const { return m_eglConfig;} private: - /** \todo Should this be non-static so we can use additional displays? */ + //Can be static because different displays returne the same handle static EGLDisplay ms_eglDisplay; QSurfaceFormat m_windowFormat; @@ -85,9 +84,7 @@ private: EGLConfig m_eglConfig; EGLContext m_eglContext; - EGLSurface m_eglSurface; - - QAtomicInt m_newSurfaceRequested; + EGLSurface m_currentEglSurface; static EGLint *contextAttrs(); }; diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index feb05e3093..fa9961ccce 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -40,7 +40,9 @@ ****************************************************************************/ #include "qqnxintegration.h" +#if defined(QQNX_SCREENEVENTTHREAD) #include "qqnxscreeneventthread.h" +#endif #include "qqnxnativeinterface.h" #include "qqnxrasterbackingstore.h" #include "qqnxscreen.h" @@ -84,6 +86,8 @@ #include <qpa/qplatformwindow.h> #include <qpa/qwindowsysteminterface.h> +#include <QtGui/private/qguiapplication_p.h> + #if !defined(QT_NO_OPENGL) #include "qqnxglcontext.h" #include <QtGui/QOpenGLContext> @@ -107,9 +111,20 @@ QT_BEGIN_NAMESPACE QQnxWindowMapper QQnxIntegration::ms_windowMapper; QMutex QQnxIntegration::ms_windowMapperMutex; -QQnxIntegration::QQnxIntegration() +static inline QQnxIntegration::Options parseOptions(const QStringList ¶mList) +{ + QQnxIntegration::Options options = QQnxIntegration::NoOptions; + if (!paramList.contains(QLatin1String("no-fullscreen"))) { + options |= QQnxIntegration::FullScreenApplication; + } + return options; +} + +QQnxIntegration::QQnxIntegration(const QStringList ¶mList) : QPlatformIntegration() +#if defined(QQNX_SCREENEVENTTHREAD) , m_screenEventThread(0) +#endif , m_navigatorEventHandler(new QQnxNavigatorEventHandler()) , m_virtualKeyboard(0) #if defined(QQNX_PPS) @@ -134,6 +149,7 @@ QQnxIntegration::QQnxIntegration() #if !defined(QT_NO_DRAGANDDROP) , m_drag(new QSimpleDrag()) #endif + , m_options(parseOptions(paramList)) { qIntegrationDebug() << Q_FUNC_INFO; // Open connection to QNX composition manager @@ -185,8 +201,13 @@ QQnxIntegration::QQnxIntegration() #if defined(Q_OS_BLACKBERRY) QQnxVirtualKeyboardBps* virtualKeyboardBps = new QQnxVirtualKeyboardBps; - m_bpsEventFilter = new QQnxBpsEventFilter(m_navigatorEventHandler, - (m_screenEventThread ? 0 : m_screenEventHandler), virtualKeyboardBps); + +#if defined(QQNX_SCREENEVENTTHREAD) + m_bpsEventFilter = new QQnxBpsEventFilter(m_navigatorEventHandler, 0, virtualKeyboardBps); +#else + m_bpsEventFilter = new QQnxBpsEventFilter(m_navigatorEventHandler, m_screenEventHandler, virtualKeyboardBps); +#endif + m_bpsEventFilter->installOnEventDispatcher(m_eventDispatcher); m_virtualKeyboard = virtualKeyboardBps; @@ -383,7 +404,7 @@ QPlatformDrag *QQnxIntegration::drag() const QVariant QQnxIntegration::styleHint(QPlatformIntegration::StyleHint hint) const { qIntegrationDebug() << Q_FUNC_INFO; - if (hint == ShowIsFullScreen) + if ((hint == ShowIsFullScreen) && (m_options & FullScreenApplication)) return true; return QPlatformIntegration::styleHint(hint); @@ -526,6 +547,11 @@ QQnxScreen *QQnxIntegration::primaryDisplay() const return m_screens.first(); } +QQnxIntegration::Options QQnxIntegration::options() const +{ + return m_options; +} + bool QQnxIntegration::supportsNavigatorEvents() const { // If QQNX_PPS or Q_OS_BLACKBERRY is defined then we have navigator diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index e3eb9e06ba..dd8973b767 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -51,7 +51,9 @@ QT_BEGIN_NAMESPACE class QQnxBpsEventFilter; +#if defined(QQNX_SCREENEVENTTHREAD) class QQnxScreenEventThread; +#endif class QQnxFileDialogHelper; class QQnxNativeInterface; class QQnxWindow; @@ -80,7 +82,12 @@ typedef QHash<screen_window_t, QWindow *> QQnxWindowMapper; class QQnxIntegration : public QPlatformIntegration { public: - QQnxIntegration(); + enum Option { // Options to be passed on command line. + NoOptions = 0x0, + FullScreenApplication = 0x1 + }; + Q_DECLARE_FLAGS(Options, Option) + explicit QQnxIntegration(const QStringList ¶mList); ~QQnxIntegration(); bool hasCapability(QPlatformIntegration::Capability cap) const; @@ -129,6 +136,8 @@ public: void createDisplay(screen_display_t display, bool isPrimary); void removeDisplay(QQnxScreen *screen); QQnxScreen *primaryDisplay() const; + Options options() const; + private: void createDisplays(); void destroyDisplays(); @@ -137,7 +146,9 @@ private: static void removeWindow(screen_window_t qnxWindow); screen_context_t m_screenContext; +#if defined(QQNX_SCREENEVENTTHREAD) QQnxScreenEventThread *m_screenEventThread; +#endif QQnxNavigatorEventHandler *m_navigatorEventHandler; QQnxAbstractVirtualKeyboard *m_virtualKeyboard; #if defined(QQNX_PPS) @@ -164,6 +175,8 @@ private: static QQnxWindowMapper ms_windowMapper; static QMutex ms_windowMapperMutex; + const Options m_options; + friend class QQnxWindow; }; diff --git a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp index 6a7a4d0944..1da3cd5446 100644 --- a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp +++ b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp @@ -91,6 +91,11 @@ void QQnxRasterBackingStore::flush(QWindow *window, const QRegion ®ion, const if (window) targetWindow = static_cast<QQnxWindow *>(window->handle()); + // we only need to flush the platformWindow backing store, since this is + // the buffer where all drawing operations of all windows, including the + // child windows, are performed; conceptually ,child windows have no buffers + // (actually they do have a 1x1 placeholder buffer due to libscreen limitations), + // since Qt will only draw to the backing store of the top-level window. QQnxWindow *platformWindow = this->platformWindow(); if (!targetWindow || targetWindow == platformWindow) { @@ -108,28 +113,6 @@ void QQnxRasterBackingStore::flush(QWindow *window, const QRegion ®ion, const // update the display with newly rendered content platformWindow->post(region); - } else if (targetWindow) { - - // The contents of the backing store should be flushed to a different window than the - // window which owns the buffer. - // This typically happens for child windows, since child windows share a backing store with - // their top-level window (TLW). - // Simply copy the buffer over to the child window, to emulate a painting operation, and - // then post the window. - // - // ### Note that because of the design in the QNX QPA plugin, each window has its own buffers, - // even though they might share a backing store. This is unneeded overhead, but I don't think - // libscreen allows to have windows without buffers, or does it? - - // We assume that the TLW has been flushed previously and that no changes were made to the - // backing store inbetween (### does Qt guarantee this?) - - targetWindow->adjustBufferSize(); - targetWindow->blitFrom(platformWindow, offset, region); - targetWindow->post(region); - - } else { - qWarning() << Q_FUNC_INFO << "flush() called without a valid window!"; } m_hasUnflushedPaintOperations = false; diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index 855da13681..69e672aefc 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -85,7 +85,9 @@ static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { if (val[0] > 0 && val[1] > 0) return QSize(val[0], val[1]); - qWarning("QQnxScreen: screen_get_display_property_iv() reported an invalid physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE environment variable.", val[0], val[1]); + qScreenDebug("QQnxScreen: screen_get_display_property_iv() reported an invalid " + "physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE " + "environment variable.", val[0], val[1]); const QString envPhySizeStr = qgetenv("QQNX_PHYSICAL_SCREEN_SIZE"); if (!envPhySizeStr.isEmpty()) { @@ -94,7 +96,9 @@ static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { const int envHeight = envPhySizeStrList.size() == 2 ? envPhySizeStrList[1].toInt() : -1; if (envWidth <= 0 || envHeight <= 0) { - qFatal("QQnxScreen: The value of QQNX_PHYSICAL_SCREEN_SIZE must be in the format \"width,height\" in mm, with width, height > 0. Example: QQNX_PHYSICAL_SCREEN_SIZE=150,90"); + qFatal("QQnxScreen: The value of QQNX_PHYSICAL_SCREEN_SIZE must be in the format " + "\"width,height\" in mm, with width, height > 0. " + "Example: QQNX_PHYSICAL_SCREEN_SIZE=150,90"); return QSize(150, 90); } @@ -103,11 +107,14 @@ static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { #if defined(QQNX_PHYSICAL_SCREEN_SIZE_DEFINED) const QSize defSize(QQNX_PHYSICAL_SCREEN_WIDTH, QQNX_PHYSICAL_SCREEN_HEIGHT); - qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Falling back to defines QQNX_PHYSICAL_SCREEN_WIDTH/QQNX_PHYSICAL_SCREEN_HEIGHT (%dx%d)", defSize.width(), defSize.height()); + qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Falling back to defines " + "QQNX_PHYSICAL_SCREEN_WIDTH/QQNX_PHYSICAL_SCREEN_HEIGHT (%dx%d)", + defSize.width(), defSize.height()); return defSize; #else if (primaryScreen) - qFatal("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Could not determine physical screen size."); + qFatal("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. " + "Could not determine physical screen size."); return QSize(150, 90); #endif } @@ -119,7 +126,6 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, m_posted(false), m_keyboardHeight(0), m_nativeOrientation(Qt::PrimaryOrientation), - m_platformContext(0), m_cursor(new QQnxCursor()) { qScreenDebug() << Q_FUNC_INFO; @@ -475,9 +481,14 @@ void QQnxScreen::updateHierarchy() int topZorder; errno = 0; - result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder); - if (result != 0) - qFatal("QQnxScreen: failed to query root window z-order, errno=%d", errno); + if (isPrimaryScreen()) { + result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder); + if (result != 0) + qFatal("QQnxScreen: failed to query root window z-order, errno=%d", errno); + } else { + topZorder = 0; //We do not need z ordering on the secondary screen, because only one window + //is supported there + } topZorder++; // root window has the lowest z-order in the windowgroup @@ -638,6 +649,9 @@ void QQnxScreen::activateWindowGroup(const QByteArray &id) if (!window) return; + Q_FOREACH (QQnxWindow *childWindow, m_childWindows) + childWindow->setExposed(true); + QWindowSystemInterface::handleWindowActivated(window); } @@ -648,6 +662,9 @@ void QQnxScreen::deactivateWindowGroup(const QByteArray &id) if (!rootWindow() || id != rootWindow()->groupName()) return; + Q_FOREACH (QQnxWindow *childWindow, m_childWindows) + childWindow->setExposed(false); + QWindowSystemInterface::handleWindowActivated(0); } diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index 42c490d443..fd97e2e58f 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -137,7 +137,6 @@ private: Qt::ScreenOrientation m_nativeOrientation; QRect m_initialGeometry; QRect m_currentGeometry; - QPlatformOpenGLContext *m_platformContext; QList<QQnxWindow *> m_childWindows; QList<screen_window_t> m_overlays; diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index c2d0e3e41c..c869d29c99 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -469,8 +469,14 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event) qScreenEventDebug() << Q_FUNC_INFO << "display attachment is now:" << isAttached; QQnxScreen *screen = m_qnxIntegration->screenForNative(nativeDisplay); + if (!screen) { if (isAttached) { + int val[2]; + screen_get_display_property_iv(nativeDisplay, SCREEN_PROPERTY_SIZE, val); + if (val[0] == 0 && val[1] == 0) //If screen size is invalid, wait for the next event + return; + qScreenEventDebug() << "creating new QQnxScreen for newly attached display"; m_qnxIntegration->createDisplay(nativeDisplay, false /* not primary, we assume */); } diff --git a/src/plugins/platforms/qnx/qqnxtheme.cpp b/src/plugins/platforms/qnx/qqnxtheme.cpp index 733b7223b6..37c1079441 100644 --- a/src/plugins/platforms/qnx/qqnxtheme.cpp +++ b/src/plugins/platforms/qnx/qqnxtheme.cpp @@ -58,10 +58,8 @@ QQnxTheme::~QQnxTheme() bool QQnxTheme::usePlatformNativeDialog(DialogType type) const { -#if defined(Q_OS_BLACKBERRY_TABLET) if (type == QPlatformTheme::FileDialog) return true; -#endif #if !defined(QT_NO_COLORDIALOG) if (type == QPlatformTheme::ColorDialog) return false; @@ -76,10 +74,8 @@ bool QQnxTheme::usePlatformNativeDialog(DialogType type) const QPlatformDialogHelper *QQnxTheme::createPlatformDialogHelper(DialogType type) const { switch (type) { -#if defined(Q_OS_BLACKBERRY_TABLET) case QPlatformTheme::FileDialog: return new QQnxFileDialogHelper(m_integration); -#endif #if !defined(QT_NO_COLORDIALOG) case QPlatformTheme::ColorDialog: #endif diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index fb8e8075ad..61b13099fd 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -72,13 +72,16 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context) m_window(0), m_currentBufferIndex(-1), m_previousBufferIndex(-1), -#if !defined(QT_NO_OPENGL) - m_platformOpenGLContext(0), -#endif m_screen(0), m_parentWindow(0), m_visible(false), + m_exposed(true), m_windowState(Qt::WindowNoState), +#if !defined(QT_NO_OPENGL) + m_platformOpenGLContext(0), + m_newSurfaceRequested(true), + m_eglSurface(EGL_NO_SURFACE), +#endif m_requestedBufferSize(window->geometry().size()) { qWindowDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size(); @@ -86,7 +89,11 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context) // Create child QNX window errno = 0; - result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + if (static_cast<QQnxScreen *>(window->screen()->handle())->isPrimaryScreen()) { + result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + } else { + result = screen_create_window(&m_window, m_screenContext); + } if (result != 0) qFatal("QQnxWindow: failed to create window, errno=%d", errno); @@ -172,6 +179,11 @@ QQnxWindow::~QQnxWindow() // Cleanup QNX window and its buffers screen_destroy_window(m_window); + +#if !defined(QT_NO_OPENGL) + // Cleanup EGL surface if it exists + destroyEGLSurface(); +#endif } void QQnxWindow::setGeometry(const QRect &rect) @@ -180,16 +192,16 @@ void QQnxWindow::setGeometry(const QRect &rect) #if !defined(QT_NO_OPENGL) // If this is an OpenGL window we need to request that the GL context updates - // the EGLsurface on which it is rendering. The surface will be recreated the - // next time QQnxGLContext::makeCurrent() is called. + // the EGLsurface on which it is rendering. { // We want the setting of the atomic bool in the GL context to be atomic with // setting m_requestedBufferSize and therefore extended the scope to include // that test. const QMutexLocker locker(&m_mutex); m_requestedBufferSize = rect.size(); - if (m_platformOpenGLContext != 0 && bufferSize() != rect.size()) - m_platformOpenGLContext->requestSurfaceChange(); + if (m_platformOpenGLContext != 0 && bufferSize() != rect.size()) { + m_newSurfaceRequested.testAndSetRelease(false, true); + } } #endif @@ -323,9 +335,19 @@ void QQnxWindow::setOpacity(qreal level) // the transparency will look wrong... } +void QQnxWindow::setExposed(bool exposed) +{ + qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "expose =" << exposed; + + if (m_exposed != exposed) { + m_exposed = exposed; + QWindowSystemInterface::handleExposeEvent(window(), window()->geometry()); + } +} + bool QQnxWindow::isExposed() const { - return m_visible; + return m_visible && m_exposed; } QSize QQnxWindow::requestedBufferSize() const @@ -336,6 +358,9 @@ QSize QQnxWindow::requestedBufferSize() const void QQnxWindow::adjustBufferSize() { + if (m_parentWindow) + return; + const QSize windowSize = window()->size(); if (windowSize != bufferSize()) setBufferSize(windowSize); @@ -526,8 +551,12 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) if (m_screen == platformScreen) return; - if (m_screen) + if (m_screen) { + qWindowDebug() << Q_FUNC_INFO << "Moving window to different screen"; m_screen->removeWindow(this); + screen_leave_window_group(m_window); + } + platformScreen->addWindow(this); m_screen = platformScreen; @@ -538,17 +567,20 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) if (result != 0) qFatal("QQnxWindow: failed to set window display, errno=%d", errno); - // Add window to display's window group - errno = 0; - result = screen_join_window_group(m_window, platformScreen->windowGroupName()); - if (result != 0) - qFatal("QQnxWindow: failed to join window group, errno=%d", errno); - Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { - // Only subwindows and tooltips need necessarily be moved to another display with the window. - if ((window()->type() & Qt::WindowType_Mask) == Qt::SubWindow || - (window()->type() & Qt::WindowType_Mask) == Qt::ToolTip) - childWindow->setScreen(platformScreen); + if (m_screen->isPrimaryScreen()) { + // Add window to display's window group + errno = 0; + result = screen_join_window_group(m_window, platformScreen->windowGroupName()); + if (result != 0) + qFatal("QQnxWindow: failed to join window group, errno=%d", errno); + + Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { + // Only subwindows and tooltips need necessarily be moved to another display with the window. + if ((window()->type() & Qt::WindowType_Mask) == Qt::SubWindow || + (window()->type() & Qt::WindowType_Mask) == Qt::ToolTip) + childWindow->setScreen(platformScreen); + } } m_screen->updateHierarchy(); @@ -586,8 +618,18 @@ void QQnxWindow::setParent(const QPlatformWindow *window) setScreen(m_parentWindow->m_screen); m_parentWindow->m_childWindows.push_back(this); + + // we don't need any buffers, since + // Qt will draw to the parent TLW + // backing store. + setBufferSize(QSize(1, 1)); } else { m_screen->addWindow(this); + + // recreate buffers, in case the + // window has been reparented and + // becomes a TLW + adjustBufferSize(); } m_screen->updateHierarchy(); @@ -646,6 +688,12 @@ void QQnxWindow::setWindowState(Qt::WindowState state) applyWindowState(); } +void QQnxWindow::propagateSizeHints() +{ + // nothing to do; silence base class warning + qWindowDebug() << Q_FUNC_INFO << ": ignored"; +} + void QQnxWindow::gainedFocus() { qWindowDebug() << Q_FUNC_INFO << "window =" << window(); @@ -711,6 +759,79 @@ void QQnxWindow::minimize() #endif } +#if !defined(QT_NO_OPENGL) +void QQnxWindow::createEGLSurface() +{ + // Fetch the surface size from the window and update + // the window's buffers before we create the EGL surface + const QSize surfaceSize = requestedBufferSize(); + if (!surfaceSize.isValid()) { + qFatal("QQNX: Trying to create 0 size EGL surface. " + "Please set a valid window size before calling QOpenGLContext::makeCurrent()"); + } + setBufferSize(surfaceSize); + + // Post root window, in case it hasn't been posted yet, to make it appear. + screen()->onWindowPost(0); + + const EGLint eglSurfaceAttrs[] = + { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE + }; + + qWindowDebug() << "Creating EGL surface" << platformOpenGLContext()->getEglDisplay() + << platformOpenGLContext()->getEglConfig(); + // Create EGL surface + m_eglSurface = eglCreateWindowSurface(platformOpenGLContext()->getEglDisplay() + , platformOpenGLContext()->getEglConfig(), + (EGLNativeWindowType) m_window, eglSurfaceAttrs); + if (m_eglSurface == EGL_NO_SURFACE) { + QQnxGLContext::checkEGLError("eglCreateWindowSurface"); + qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError()); + } +} + +void QQnxWindow::destroyEGLSurface() +{ + // Destroy EGL surface if it exists + if (m_eglSurface != EGL_NO_SURFACE) { + EGLBoolean eglResult = eglDestroySurface(platformOpenGLContext()->getEglDisplay(), m_eglSurface); + if (eglResult != EGL_TRUE) + qFatal("QQNX: failed to destroy EGL surface, err=%d", eglGetError()); + } + + m_eglSurface = EGL_NO_SURFACE; +} + +void QQnxWindow::swapEGLBuffers() +{ + qWindowDebug() << Q_FUNC_INFO; + // Set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) + qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); + + // Post EGL surface to window + eglResult = eglSwapBuffers(m_platformOpenGLContext->getEglDisplay(), m_eglSurface); + if (eglResult != EGL_TRUE) + qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError()); +} + +EGLSurface QQnxWindow::getSurface() +{ + if (m_newSurfaceRequested.testAndSetOrdered(true, false)) { + if (m_eglSurface != EGL_NO_SURFACE) { + platformOpenGLContext()->doneCurrent(); + destroyEGLSurface(); + } + createEGLSurface(); + } + + return m_eglSurface; +} +#endif + void QQnxWindow::updateZorder(int &topZorder) { errno = 0; diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index 63d5dc0979..cd1980d994 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -102,6 +102,9 @@ public: void lower(); void requestActivateWindow(); void setWindowState(Qt::WindowState state); + void setExposed(bool exposed); + + void propagateSizeHints(); void gainedFocus(); @@ -118,6 +121,13 @@ public: void blitFrom(QQnxWindow *sourceWindow, const QPoint &sourceOffset, const QRegion &targetRegion); void minimize(); +#if !defined(QT_NO_OPENGL) + void createEGLSurface(); + void destroyEGLSurface(); + void swapEGLBuffers(); + EGLSurface getSurface(); +#endif + private: QRect setGeometryHelper(const QRect &rect); void removeFromParent(); @@ -145,13 +155,11 @@ private: QRegion m_previousDirty; QRegion m_scrolled; -#if !defined(QT_NO_OPENGL) - QQnxGLContext *m_platformOpenGLContext; -#endif QQnxScreen *m_screen; QList<QQnxWindow*> m_childWindows; QQnxWindow *m_parentWindow; bool m_visible; + bool m_exposed; QRect m_unmaximizedGeometry; Qt::WindowState m_windowState; @@ -162,6 +170,13 @@ private: // EGL surface. All of this has to be done from the thread that is calling // QQnxGLContext::makeCurrent() mutable QMutex m_mutex; + +#if !defined(QT_NO_OPENGL) + QQnxGLContext *m_platformOpenGLContext; + QAtomicInt m_newSurfaceRequested; + EGLSurface m_eglSurface; +#endif + QSize m_requestedBufferSize; }; |