From 87366cc7ab7e2e5f23614a8501f33b6cb0b65f97 Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Thu, 19 Jan 2012 20:10:43 +0000 Subject: Initial import of the Blackberry QPA plugin for Qt5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is dependent upon the following Change Id's: I5ebcffb7153f4216d69921d4818051e6b3d14d8a Iec065f528f5edd848be580807a607488dc2e401f Change-Id: I234e3c4272d7474d8f8e20fc4fea20d95c829cb5 Reviewed-by: Kevin Krammer Reviewed-by: Jørgen Lind --- src/plugins/platforms/blackberry/blackberry.pro | 71 + src/plugins/platforms/blackberry/main.cpp | 72 + src/plugins/platforms/blackberry/qbbbuffer.cpp | 165 ++ src/plugins/platforms/blackberry/qbbbuffer.h | 74 + src/plugins/platforms/blackberry/qbbclipboard.cpp | 132 ++ src/plugins/platforms/blackberry/qbbclipboard.h | 67 + .../platforms/blackberry/qbbeventthread.cpp | 559 +++++++ src/plugins/platforms/blackberry/qbbeventthread.h | 90 ++ .../platforms/blackberry/qbbglbackingstore.cpp | 189 +++ .../platforms/blackberry/qbbglbackingstore.h | 95 ++ src/plugins/platforms/blackberry/qbbglcontext.cpp | 356 ++++ src/plugins/platforms/blackberry/qbbglcontext.h | 93 ++ .../platforms/blackberry/qbbinputcontext_imf.cpp | 1696 ++++++++++++++++++++ .../platforms/blackberry/qbbinputcontext_imf.h | 132 ++ .../platforms/blackberry/qbbinputcontext_noimf.cpp | 187 +++ .../platforms/blackberry/qbbinputcontext_noimf.h | 84 + .../platforms/blackberry/qbbintegration.cpp | 293 ++++ src/plugins/platforms/blackberry/qbbintegration.h | 119 ++ .../platforms/blackberry/qbbkeytranslator.h | 269 ++++ .../platforms/blackberry/qbbnavigatorthread.cpp | 279 ++++ .../platforms/blackberry/qbbnavigatorthread.h | 78 + .../platforms/blackberry/qbbrasterbackingstore.cpp | 166 ++ .../platforms/blackberry/qbbrasterbackingstore.h | 81 + src/plugins/platforms/blackberry/qbbrootwindow.cpp | 257 +++ src/plugins/platforms/blackberry/qbbrootwindow.h | 81 + src/plugins/platforms/blackberry/qbbscreen.cpp | 315 ++++ src/plugins/platforms/blackberry/qbbscreen.h | 121 ++ .../platforms/blackberry/qbbvirtualkeyboard.cpp | 500 ++++++ .../platforms/blackberry/qbbvirtualkeyboard.h | 130 ++ src/plugins/platforms/blackberry/qbbwindow.cpp | 665 ++++++++ src/plugins/platforms/blackberry/qbbwindow.h | 133 ++ src/plugins/platforms/platforms.pro | 4 + 32 files changed, 7553 insertions(+) create mode 100644 src/plugins/platforms/blackberry/blackberry.pro create mode 100644 src/plugins/platforms/blackberry/main.cpp create mode 100644 src/plugins/platforms/blackberry/qbbbuffer.cpp create mode 100644 src/plugins/platforms/blackberry/qbbbuffer.h create mode 100644 src/plugins/platforms/blackberry/qbbclipboard.cpp create mode 100644 src/plugins/platforms/blackberry/qbbclipboard.h create mode 100644 src/plugins/platforms/blackberry/qbbeventthread.cpp create mode 100644 src/plugins/platforms/blackberry/qbbeventthread.h create mode 100644 src/plugins/platforms/blackberry/qbbglbackingstore.cpp create mode 100644 src/plugins/platforms/blackberry/qbbglbackingstore.h create mode 100644 src/plugins/platforms/blackberry/qbbglcontext.cpp create mode 100644 src/plugins/platforms/blackberry/qbbglcontext.h create mode 100644 src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp create mode 100644 src/plugins/platforms/blackberry/qbbinputcontext_imf.h create mode 100644 src/plugins/platforms/blackberry/qbbinputcontext_noimf.cpp create mode 100644 src/plugins/platforms/blackberry/qbbinputcontext_noimf.h create mode 100644 src/plugins/platforms/blackberry/qbbintegration.cpp create mode 100644 src/plugins/platforms/blackberry/qbbintegration.h create mode 100644 src/plugins/platforms/blackberry/qbbkeytranslator.h create mode 100644 src/plugins/platforms/blackberry/qbbnavigatorthread.cpp create mode 100644 src/plugins/platforms/blackberry/qbbnavigatorthread.h create mode 100644 src/plugins/platforms/blackberry/qbbrasterbackingstore.cpp create mode 100644 src/plugins/platforms/blackberry/qbbrasterbackingstore.h create mode 100644 src/plugins/platforms/blackberry/qbbrootwindow.cpp create mode 100644 src/plugins/platforms/blackberry/qbbrootwindow.h create mode 100644 src/plugins/platforms/blackberry/qbbscreen.cpp create mode 100644 src/plugins/platforms/blackberry/qbbscreen.h create mode 100644 src/plugins/platforms/blackberry/qbbvirtualkeyboard.cpp create mode 100644 src/plugins/platforms/blackberry/qbbvirtualkeyboard.h create mode 100644 src/plugins/platforms/blackberry/qbbwindow.cpp create mode 100644 src/plugins/platforms/blackberry/qbbwindow.h (limited to 'src') diff --git a/src/plugins/platforms/blackberry/blackberry.pro b/src/plugins/platforms/blackberry/blackberry.pro new file mode 100644 index 0000000000..94b9c5dbc8 --- /dev/null +++ b/src/plugins/platforms/blackberry/blackberry.pro @@ -0,0 +1,71 @@ +TARGET = blackberry +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms +QT += opengl opengl-private platformsupport platformsupport-private widgets-private + +# Uncomment this to build with support for IMF once it becomes available in the BBNDK +#CONFIG += qbb_imf + +# Uncomment these to enable debugging output for various aspects of the plugin +#DEFINES += QBBBUFFER_DEBUG +#DEFINES += QBBCLIPBOARD_DEBUG +#DEFINES += QBBEVENTTHREAD_DEBUG +#DEFINES += QBBGLBACKINGSTORE_DEBUG +#DEFINES += QBBGLCONTEXT_DEBUG +#DEFINES += QBBINPUTCONTEXT_DEBUG +#DEFINES += QBBINPUTCONTEXT_IMF_EVENT_DEBUG +#DEFINES += QBBINTEGRATION_DEBUG +#DEFINES += QBBNAVIGATORTHREAD_DEBUG +#DEFINES += QBBRASTERBACKINGSTORE_DEBUG +#DEFINES += QBBROOTWINDOW_DEBUG +#DEFINES += QBBSCREEN_DEBUG +#DEFINES += QBBVIRTUALKEYBOARD_DEBUG +#DEFINES += QBBWINDOW_DEBUG + +SOURCES = main.cpp \ + qbbbuffer.cpp \ + qbbeventthread.cpp \ + qbbglcontext.cpp \ + qbbglbackingstore.cpp \ + qbbintegration.cpp \ + qbbnavigatorthread.cpp \ + qbbscreen.cpp \ + qbbwindow.cpp \ + qbbrasterbackingstore.cpp \ + qbbvirtualkeyboard.cpp \ + qbbclipboard.cpp \ + qbbrootwindow.cpp + +HEADERS = qbbbuffer.h \ + qbbeventthread.h \ + qbbkeytranslator.h \ + qbbintegration.h \ + qbbnavigatorthread.h \ + qbbglcontext.h \ + qbbglbackingstore.h \ + qbbscreen.h \ + qbbwindow.h \ + qbbrasterbackingstore.h \ + qbbvirtualkeyboard.h \ + qbbclipboard.h \ + qbbrootwindow.h + +CONFIG(qbb_imf) { + DEFINES += QBB_IMF + HEADERS += qbbinputcontext_imf.h + SOURCES += qbbinputcontext_imf.cpp +} else { + HEADERS += qbbinputcontext_noimf.h + SOURCES += qbbinputcontext_noimf.cpp +} + +QMAKE_CXXFLAGS += -I./private + +LIBS += -lpps -lscreen -lEGL -lclipboard + +include (../../../platformsupport/eglconvenience/eglconvenience.pri) +include (../../../platformsupport/fontdatabases/fontdatabases.pri) + +target.path += $$[QT_INSTALL_PLUGINS]/platforms +INSTALLS += target diff --git a/src/plugins/platforms/blackberry/main.cpp b/src/plugins/platforms/blackberry/main.cpp new file mode 100644 index 0000000000..b9e09c0384 --- /dev/null +++ b/src/plugins/platforms/blackberry/main.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qbbintegration.h" + +QT_BEGIN_NAMESPACE + +class QBBIntegrationPlugin : public QPlatformIntegrationPlugin +{ +public: + QStringList keys() const; + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QStringList QBBIntegrationPlugin::keys() const +{ + QStringList list; + list << QLatin1String("blackberry"); + return list; +} + +QPlatformIntegration *QBBIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == QLatin1String("blackberry")) + return new QBBIntegration; + + return 0; +} + +Q_EXPORT_PLUGIN2(blackberry, QBBIntegrationPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbbuffer.cpp b/src/plugins/platforms/blackberry/qbbbuffer.cpp new file mode 100644 index 0000000000..c4ac04898d --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbbuffer.cpp @@ -0,0 +1,165 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbbuffer.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QBBBuffer::QBBBuffer() + : m_buffer(0) +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::QBBBuffer - empty"; +#endif +} + +QBBBuffer::QBBBuffer(screen_buffer_t buffer) + : m_buffer(buffer) +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::QBBBuffer - normal"; +#endif + + // Get size of buffer + errno = 0; + int size[2]; + int result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_BUFFER_SIZE, size); + if (result != 0) { + qFatal("QBB: failed to query buffer size, errno=%d", errno); + } + + // Get stride of buffer + errno = 0; + int stride; + result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride); + if (result != 0) { + qFatal("QBB: failed to query buffer stride, errno=%d", errno); + } + + // Get access to buffer's data + errno = 0; + uchar *dataPtr = 0; + result = screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, (void **)&dataPtr); + if (result != 0) { + qFatal("QBB: failed to query buffer pointer, errno=%d", errno); + } + if (dataPtr == NULL) { + qFatal("QBB: buffer pointer is NULL, errno=%d", errno); + } + + // Get format of buffer + errno = 0; + int screenFormat; + result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_FORMAT, &screenFormat); + if (result != 0) { + qFatal("QBB: failed to query buffer format, errno=%d", errno); + } + + // Convert screen format to QImage format + QImage::Format imageFormat = QImage::Format_Invalid; + switch (screenFormat) { + case SCREEN_FORMAT_RGBX4444: + imageFormat = QImage::Format_RGB444; + break; + case SCREEN_FORMAT_RGBA4444: + imageFormat = QImage::Format_ARGB4444_Premultiplied; + break; + case SCREEN_FORMAT_RGBX5551: + imageFormat = QImage::Format_RGB555; + break; + case SCREEN_FORMAT_RGB565: + imageFormat = QImage::Format_RGB16; + break; + case SCREEN_FORMAT_RGBX8888: + imageFormat = QImage::Format_RGB32; + break; + case SCREEN_FORMAT_RGBA8888: + imageFormat = QImage::Format_ARGB32_Premultiplied; + break; + default: + qFatal("QBB: unsupported buffer format, format=%d", screenFormat); + } + + // wrap buffer in an image + m_image = QImage(dataPtr, size[0], size[1], stride, imageFormat); +} + +QBBBuffer::QBBBuffer(const QBBBuffer &other) + : m_buffer(other.m_buffer), + m_image(other.m_image) +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::QBBBuffer - copy"; +#endif +} + +QBBBuffer::~QBBBuffer() +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::~QBBBuffer"; +#endif +} + +void QBBBuffer::invalidateInCache() +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::invalidateInCache"; +#endif + + // Verify native buffer exists + if (m_buffer == 0) { + qFatal("QBB: can't invalidate cache for null buffer"); + } + + // Evict buffer's data from cache + errno = 0; + int result = msync(m_image.bits(), m_image.height() * m_image.bytesPerLine(), MS_INVALIDATE | MS_CACHE_ONLY); + if (result != 0) { + qFatal("QBB: failed to invalidate cache, errno=%d", errno); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbbuffer.h b/src/plugins/platforms/blackberry/qbbbuffer.h new file mode 100644 index 0000000000..45cedb21a8 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbbuffer.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBBUFFER_H +#define QBBBUFFER_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBBuffer +{ +public: + QBBBuffer(); + QBBBuffer(screen_buffer_t buffer); + QBBBuffer(const QBBBuffer &other); + virtual ~QBBBuffer(); + + screen_buffer_t nativeBuffer() const { return m_buffer; } + const QImage *image() const { return (m_buffer != NULL) ? &m_image : NULL; } + QImage *image() { return (m_buffer != NULL) ? &m_image : NULL; } + + QRect rect() const { return m_image.rect(); } + + void invalidateInCache(); + +private: + screen_buffer_t m_buffer; + QImage m_image; +}; + +QT_END_NAMESPACE + +#endif // QBBBUFFER_H diff --git a/src/plugins/platforms/blackberry/qbbclipboard.cpp b/src/plugins/platforms/blackberry/qbbclipboard.cpp new file mode 100644 index 0000000000..fce016d5ee --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbclipboard.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_CLIPBOARD + +#include "qbbclipboard.h" + +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE +static const char *typeList[] = {"text/html", "text/plain", "application/x-color"}; + +QBBClipboard::QBBClipboard() +{ + m_mimeData = 0; +} + +QBBClipboard::~QBBClipboard() +{ + delete m_mimeData; +} + +void QBBClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return; + + if (m_mimeData != data) { + delete m_mimeData; + m_mimeData = data; + } + + empty_clipboard(); + + if (data == 0) + return; + + QStringList format = data->formats(); + for (int i = 0; i < format.size(); ++i) { + QString type = format.at(i); + QByteArray buf = data->data(type); + if (!buf.size()) + continue; + + int ret = set_clipboard_data(type.toUtf8().data(), buf.size(), buf.data()); +#if defined(QBBCLIPBOARD_DEBUG) + qDebug() << "QBB: set " << type.toUtf8().data() << "to clipboard, size=" << buf.size() << ";ret=" << ret; +#else + Q_UNUSED(ret); +#endif + } +} + +void QBBClipboard::readClipboardBuff(const char *type) +{ + char *pbuffer; + if (is_clipboard_format_present(type) == 0) { + int size = get_clipboard_data(type, &pbuffer); + if (size != -1 && pbuffer) { + QString qtype = type; +#if defined(QBBCLIPBOARD_DEBUG) + qDebug() << "QBB: clipboard has " << qtype; +#endif + m_mimeData->setData(qtype, QByteArray(pbuffer, size)); + delete pbuffer; + } + } +} + +QMimeData *QBBClipboard::mimeData(QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return 0; + + if (!m_mimeData) + m_mimeData = new QMimeData(); + + m_mimeData->clear(); + + for (int i = 0; i < 3; i++) + readClipboardBuff(typeList[i]); + + return m_mimeData; +} + +QT_END_NAMESPACE +#endif //QT_NO_CLIPBOAR diff --git a/src/plugins/platforms/blackberry/qbbclipboard.h b/src/plugins/platforms/blackberry/qbbclipboard.h new file mode 100644 index 0000000000..b9de9b3e36 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbclipboard.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBCLIPBOARD_H +#define QBBCLIPBOARD_H + +#ifndef QT_NO_CLIPBOARD +#include +#include + +QT_BEGIN_NAMESPACE + +class QBBClipboard : public QPlatformClipboard +{ +public: + QBBClipboard(); + virtual ~QBBClipboard(); + virtual QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard); + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + +private: + QMimeData *m_mimeData; + void readClipboardBuff(const char *type); +}; + +QT_END_NAMESPACE + +#endif //QT_NO_CLIPBOARD +#endif //QBBCLIPBOARD_H diff --git a/src/plugins/platforms/blackberry/qbbeventthread.cpp b/src/plugins/platforms/blackberry/qbbeventthread.cpp new file mode 100644 index 0000000000..547428d1c4 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbeventthread.cpp @@ -0,0 +1,559 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbeventthread.h" +#include "qbbintegration.h" +#include "qbbkeytranslator.h" + +#if defined(QBB_IMF) +#include "qbbinputcontext_imf.h" +#else +#include "qbbinputcontext_noimf.h" +#endif + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +QBBEventThread::QBBEventThread(screen_context_t context, QPlatformScreen& screen) + : QThread(), + m_screenContext(context), + m_platformScreen(screen), + m_quit(false), + m_lastButtonState(Qt::NoButton), + m_lastMouseWindow(0) +{ + // Create a touch device + m_touchDevice = new QTouchDevice; + m_touchDevice->setType(QTouchDevice::TouchScreen); + m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure | QTouchDevice::NormalizedPosition); + QWindowSystemInterface::registerTouchDevice(m_touchDevice); + + // initialize array of touch points + for (int i = 0; i < MaximumTouchPoints; i++) { + + // map array index to id + m_touchPoints[i].id = i; + + // pressure is not supported - use default + m_touchPoints[i].pressure = 1.0; + + // nothing touching + m_touchPoints[i].state = Qt::TouchPointReleased; + } +} + +QBBEventThread::~QBBEventThread() +{ + // block until thread terminates + shutdown(); +} + +void QBBEventThread::run() +{ + screen_event_t event; + + // create screen event + errno = 0; + int result = screen_create_event(&event); + if (result) { + qFatal("QBB: failed to create event, errno=%d", errno); + } + +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: event loop started"; +#endif + + // loop indefinitely + while (!m_quit) { + + // block until screen event is available + errno = 0; + result = screen_get_event(m_screenContext, event, -1); + if (result) { + qFatal("QBB: failed to get event, errno=%d", errno); + } + + // process received event + dispatchEvent(event); + } + +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: event loop stopped"; +#endif + + // cleanup + screen_destroy_event(event); +} + +void QBBEventThread::shutdown() +{ + screen_event_t event; + + // create screen event + errno = 0; + int result = screen_create_event(&event); + if (result) { + qFatal("QBB: failed to create event, errno=%d", errno); + } + + // set the event type as user + errno = 0; + int type = SCREEN_EVENT_USER; + result = screen_set_event_property_iv(event, SCREEN_PROPERTY_TYPE, &type); + if (result) { + qFatal("QBB: failed to set event type, errno=%d", errno); + } + + // NOTE: ignore SCREEN_PROPERTY_USER_DATA; treat all user events as shutdown events + + // post event to event loop so it will wake up and die + errno = 0; + result = screen_send_event(m_screenContext, event, getpid()); + if (result) { + qFatal("QBB: failed to set event type, errno=%d", errno); + } + + // cleanup + screen_destroy_event(event); + +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: event loop shutdown begin"; +#endif + + // block until thread terminates + wait(); + +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: event loop shutdown end"; +#endif +} + +void QBBEventThread::dispatchEvent(screen_event_t event) +{ + // get the event type + errno = 0; + int qnxType; + int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType); + if (result) { + qFatal("QBB: failed to query event type, errno=%d", errno); + } + + switch (qnxType) { + case SCREEN_EVENT_MTOUCH_TOUCH: + case SCREEN_EVENT_MTOUCH_MOVE: + case SCREEN_EVENT_MTOUCH_RELEASE: + handleTouchEvent(event, qnxType); + break; + + case SCREEN_EVENT_KEYBOARD: + handleKeyboardEvent(event); + break; + + case SCREEN_EVENT_POINTER: + handlePointerEvent(event); + break; + + case SCREEN_EVENT_CLOSE: + handleCloseEvent(event); + break; + + case SCREEN_EVENT_USER: + // treat all user events as shutdown requests +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: QNX user event"; +#endif + m_quit = true; + break; + + default: + // event ignored +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: QNX unknown event"; +#endif + break; + } +} + +void QBBEventThread::handleKeyboardEvent(screen_event_t event) +{ + // get flags of key event + errno = 0; + int flags; + int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_FLAGS, &flags); + if (result) { + qFatal("QBB: failed to query event flags, errno=%d", errno); + } + + // get key code + errno = 0; + int sym; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SYM, &sym); + if (result) { + qFatal("QBB: failed to query event sym, errno=%d", errno); + } + + int modifiers; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_MODIFIERS, &modifiers); + if (result) { + qFatal("QBB: failed to query event modifiers, errno=%d", errno); + } + + int scan; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SCAN, &scan); + if (result) { + qFatal("QBB: failed to query event modifiers, errno=%d", errno); + } + + int cap; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap); + if (result) { + qFatal("QBB: failed to query event cap, errno=%d", errno); + } + + injectKeyboardEvent(flags, sym, modifiers, scan, cap); +} + +void QBBEventThread::injectKeyboardEvent(int flags, int sym, int modifiers, int scan, int cap) +{ + Q_UNUSED(scan); + + Qt::KeyboardModifiers qtMod = Qt::NoModifier; + if (modifiers & KEYMOD_SHIFT) + qtMod |= Qt::ShiftModifier; + if (modifiers & KEYMOD_CTRL) + qtMod |= Qt::ControlModifier; + if (modifiers & KEYMOD_ALT) + qtMod |= Qt::AltModifier; + + // determine event type + QEvent::Type type = (flags & KEY_DOWN) ? QEvent::KeyPress : QEvent::KeyRelease; + + // Check if the key cap is valid + if (flags & KEY_CAP_VALID) { + Qt::Key key; + QString keyStr; + + if (cap >= 0x20 && cap <= 0x0ff) { + key = Qt::Key(std::toupper(cap)); // Qt expects the CAP to be upper case. + + if ( qtMod & Qt::ControlModifier ) { + keyStr = QChar((int)(key & 0x3f)); + } else { + if (flags & KEY_SYM_VALID) { + keyStr = QChar(sym); + } + } + } else if ((cap > 0x0ff && cap < UNICODE_PRIVATE_USE_AREA_FIRST) || cap > UNICODE_PRIVATE_USE_AREA_LAST) { + key = (Qt::Key)cap; + keyStr = QChar(sym); + } else { + if (isKeypadKey(cap)) + qtMod |= Qt::KeypadModifier; // Is this right? + key = keyTranslator(cap); + } + + QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), type, key, qtMod, keyStr); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt key t=" << type << ", k=" << key << ", s=" << keyStr; +#endif + } +} + +void QBBEventThread::handlePointerEvent(screen_event_t event) +{ + errno = 0; + + // Query the window that was clicked + screen_window_t qnxWindow; + void *handle; + int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); + if (result) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + qnxWindow = static_cast(handle); + + // Query the button states + int buttonState = 0; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttonState); + if (result) { + qFatal("QBB: failed to query event button state, errno=%d", errno); + } + + // Query the window position + int windowPos[2]; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos); + if (result) { + qFatal("QBB: failed to query event window position, errno=%d", errno); + } + + // Query the screen position + int pos[2]; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos); + if (result) { + qFatal("QBB: failed to query event position, errno=%d", errno); + } + + // Query the wheel delta + int wheelDelta = 0; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheelDelta); + if (result) { + qFatal("QBB: failed to query event wheel delta, errno=%d", errno); + } + + // Map window handle to top-level QWindow + QWindow *w = QBBIntegration::window(qnxWindow); + + // Generate enter and leave events as needed. + if (qnxWindow != m_lastMouseWindow) { + QWindow *wOld = QBBIntegration::window(m_lastMouseWindow); + + if (wOld) { + QWindowSystemInterface::handleLeaveEvent(wOld); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt leave, w=" << wOld; +#endif + } + + if (w) { + QWindowSystemInterface::handleEnterEvent(w); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt enter, w=" << w; +#endif + } + } + m_lastMouseWindow = qnxWindow; + + // Apply scaling to wheel delta and invert value for Qt. We'll probably want to scale + // this via a system preference at some point. But for now this is a sane value and makes + // the wheel usable. + wheelDelta *= -10; + + // convert point to local coordinates + QPoint globalPoint(pos[0], pos[1]); + QPoint localPoint(windowPos[0], windowPos[1]); + + // Convert buttons. + Qt::MouseButtons buttons = Qt::NoButton; + if (buttonState & 1) + buttons |= Qt::LeftButton; + if (buttonState & 2) + buttons |= Qt::MidButton; + if (buttonState & 4) + buttons |= Qt::RightButton; + + if (w) { + // Inject mouse event into Qt only if something has changed. + if (m_lastGlobalMousePoint != globalPoint || + m_lastLocalMousePoint != localPoint || + m_lastButtonState != buttons) { + QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << static_cast(buttons); +#endif + } + + if (wheelDelta) { + // Screen only supports a single wheel, so we will assume Vertical orientation for + // now since that is pretty much standard. + QWindowSystemInterface::handleWheelEvent(w, localPoint, globalPoint, wheelDelta, Qt::Vertical); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt wheel, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), d=" << static_cast(wheelDelta); +#endif + } + } + + m_lastGlobalMousePoint = globalPoint; + m_lastLocalMousePoint = localPoint; + m_lastButtonState = buttons; +} + +void QBBEventThread::handleTouchEvent(screen_event_t event, int qnxType) +{ + // get display coordinates of touch + errno = 0; + int pos[2]; + int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos); + if (result) { + qFatal("QBB: failed to query event position, errno=%d", errno); + } + + // get window coordinates of touch + errno = 0; + int windowPos[2]; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos); + if (result) { + qFatal("QBB: failed to query event window position, errno=%d", errno); + } + + // determine which finger touched + errno = 0; + int touchId; + result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touchId); + if (result) { + qFatal("QBB: failed to query event touch id, errno=%d", errno); + } + + // determine which window was touched + errno = 0; + void *handle; + result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); + if (result) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + screen_window_t qnxWindow = static_cast(handle); + + // check if finger is valid + if (touchId < MaximumTouchPoints) { + + // Map window handle to top-level QWindow + QWindow *w = QBBIntegration::window(qnxWindow); + + // Generate enter and leave events as needed. + if (qnxWindow != m_lastMouseWindow) { + QWindow *wOld = QBBIntegration::window(m_lastMouseWindow); + + if (wOld) { + QWindowSystemInterface::handleLeaveEvent(wOld); + #if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt leave, w=" << wOld; + #endif + } + + if (w) { + QWindowSystemInterface::handleEnterEvent(w); + #if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt enter, w=" << w; + #endif + } + } + m_lastMouseWindow = qnxWindow; + + if (w) { + // convert primary touch to mouse event + if (touchId == 0) { + + // convert point to local coordinates + QPoint globalPoint(pos[0], pos[1]); + QPoint localPoint(windowPos[0], windowPos[1]); + + // map touch state to button state + Qt::MouseButtons buttons = (qnxType == SCREEN_EVENT_MTOUCH_RELEASE) ? Qt::NoButton : Qt::LeftButton; + + // inject event into Qt + QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << buttons; +#endif + } + + // get size of screen which contains window + QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(w); + QSizeF screenSize = platformScreen->physicalSize(); + + // update cached position of current touch point + m_touchPoints[touchId].normalPosition = QPointF( static_cast(pos[0]) / screenSize.width(), static_cast(pos[1]) / screenSize.height() ); + m_touchPoints[touchId].area = QRectF( pos[0], pos[1], 0.0, 0.0 ); + + // determine event type and update state of current touch point + QEvent::Type type = QEvent::None; + switch (qnxType) { + case SCREEN_EVENT_MTOUCH_TOUCH: + m_touchPoints[touchId].state = Qt::TouchPointPressed; + type = QEvent::TouchBegin; + break; + case SCREEN_EVENT_MTOUCH_MOVE: + m_touchPoints[touchId].state = Qt::TouchPointMoved; + type = QEvent::TouchUpdate; + break; + case SCREEN_EVENT_MTOUCH_RELEASE: + m_touchPoints[touchId].state = Qt::TouchPointReleased; + type = QEvent::TouchEnd; + break; + } + + // build list of active touch points + QList pointList; + for (int i = 0; i < MaximumTouchPoints; i++) { + if (i == touchId) { + // current touch point is always active + pointList.append(m_touchPoints[i]); + } else if (m_touchPoints[i].state != Qt::TouchPointReleased) { + // finger is down but did not move + m_touchPoints[i].state = Qt::TouchPointStationary; + pointList.append(m_touchPoints[i]); + } + } + + // inject event into Qt + QWindowSystemInterface::handleTouchEvent(w, m_touchDevice, pointList); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt touch, w=" << w << ", p=(" << pos[0] << "," << pos[1] << "), t=" << type; +#endif + } + } +} + +void QBBEventThread::handleCloseEvent(screen_event_t event) +{ + // Query the window that was closed + void *handle; + int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); + if (result != 0) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + screen_window_t qnxWindow = static_cast(handle); + + // Map window handle to top-level QWindow + QWindow *w = QBBIntegration::window(qnxWindow); + if (w != 0) { + QWindowSystemInterface::handleCloseEvent(w); + } +} + diff --git a/src/plugins/platforms/blackberry/qbbeventthread.h b/src/plugins/platforms/blackberry/qbbeventthread.h new file mode 100644 index 0000000000..afa738830c --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbeventthread.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBEVENTTHREAD_H +#define QBBEVENTTHREAD_H + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBEventThread : public QThread +{ +public: + QBBEventThread(screen_context_t context, QPlatformScreen& screen); + virtual ~QBBEventThread(); + + static void injectKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + +protected: + virtual void run(); + +private: + enum { + MaximumTouchPoints = 10 + }; + + void shutdown(); + void dispatchEvent(screen_event_t event); + void handleKeyboardEvent(screen_event_t event); + void handlePointerEvent(screen_event_t event); + void handleTouchEvent(screen_event_t event, int type); + void handleCloseEvent(screen_event_t event); + + screen_context_t m_screenContext; + QPlatformScreen& m_platformScreen; + bool m_quit; + QPoint m_lastGlobalMousePoint; + QPoint m_lastLocalMousePoint; + Qt::MouseButtons m_lastButtonState; + screen_window_t m_lastMouseWindow; + QTouchDevice *m_touchDevice; + QWindowSystemInterface::TouchPoint m_touchPoints[MaximumTouchPoints]; +}; + +QT_END_NAMESPACE + +#endif // QBBEVENTTHREAD_H diff --git a/src/plugins/platforms/blackberry/qbbglbackingstore.cpp b/src/plugins/platforms/blackberry/qbbglbackingstore.cpp new file mode 100644 index 0000000000..91b07770e1 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglbackingstore.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbglbackingstore.h" +#include "qbbglcontext.h" +#include "qbbwindow.h" +#include "qbbscreen.h" + +#include + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +QBBGLPaintDevice::QBBGLPaintDevice(QWindow *window) + : QGLPaintDevice(), + m_window(0), + m_glContext(0) +{ + m_window = static_cast(window->handle()); + + // Extract the QPlatformOpenGLContext from the window + QPlatformOpenGLContext *platformOpenGLContext = m_window->platformOpenGLContext(); + + // Convert this to a QGLContext + m_glContext = QGLContext::fromOpenGLContext(platformOpenGLContext->context()); +} + +QBBGLPaintDevice::~QBBGLPaintDevice() +{ + // Cleanup GL context + delete m_glContext; +} + +QPaintEngine *QBBGLPaintDevice::paintEngine() const +{ + // Select a paint engine based on configued OpenGL version + return qt_qgl_paint_engine(); +} + +QSize QBBGLPaintDevice::size() const +{ + // Get size of EGL surface + return m_window->geometry().size(); +} + + +QBBGLBackingStore::QBBGLBackingStore(QWindow *window) + : QPlatformBackingStore(window), + m_openGLContext(0), + m_paintDevice(0), + m_requestedSize(), + m_size() +{ +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::QBBGLBackingStore - w=" << window; +#endif + + // Create an OpenGL paint device which in turn creates a QGLContext for us + m_paintDevice = new QBBGLPaintDevice(window); + m_openGLContext = m_paintDevice->context()->contextHandle(); +} + +QBBGLBackingStore::~QBBGLBackingStore() +{ +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::~QBBGLBackingStore - w=" << window(); +#endif + + // cleanup OpenGL paint device + delete m_paintDevice; +} + +void QBBGLBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::flush - w=" << window; +#endif + + // update the display with newly rendered content + m_openGLContext->swapBuffers(window); +} + +void QBBGLBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ + Q_UNUSED(staticContents); +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::resize - w=" << window() << ", s=" << size; +#endif + // NOTE: defer resizing window buffers until next paint as + // resize() can be called multiple times before a paint occurs + m_requestedSize = size; +} + +void QBBGLBackingStore::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::beginPaint - w=" << window(); +#endif + + // resize EGL surface if window surface resized + if (m_size != m_requestedSize) { + resizeSurface(m_requestedSize); + } +} + +void QBBGLBackingStore::endPaint(const QRegion ®ion) +{ + Q_UNUSED(region); +#if defined(QBBGLBACKINGSTORE_DEBUG) + qDebug() << "QBBGLBackingStore::endPaint - w=" << window(); +#endif +} + +void QBBGLBackingStore::resizeSurface(const QSize &size) +{ + // need to destroy surface so make sure its not current + bool restoreCurrent = false; + QBBGLContext *platformContext = static_cast(m_openGLContext->handle()); + if (platformContext->isCurrent()) { + m_openGLContext->doneCurrent(); + restoreCurrent = true; + } + + // destroy old EGL surface + platformContext->destroySurface(); + + // resize window's buffers + static_cast(window()->handle())->setBufferSize(size); + + // re-create EGL surface with new size + m_size = size; + platformContext->createSurface(window()->handle()); + + // make context current again + if (restoreCurrent) { + m_openGLContext->makeCurrent(window()); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbglbackingstore.h b/src/plugins/platforms/blackberry/qbbglbackingstore.h new file mode 100644 index 0000000000..5455c5767c --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglbackingstore.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBGLBACKINGSTORE_H +#define QBBGLBACKINGSTORE_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGLContext; +class QBBGLContext; +class QBBScreen; +class QBBWindow; + +class QBBGLPaintDevice : public QGLPaintDevice +{ +public: + QBBGLPaintDevice(QWindow *window); + virtual ~QBBGLPaintDevice(); + + virtual QPaintEngine *paintEngine() const; + virtual QSize size() const; + virtual QGLContext *context() const { return m_glContext; } + +private: + QBBWindow *m_window; + QGLContext *m_glContext; +}; + +class QBBGLBackingStore : public QPlatformBackingStore +{ +public: + QBBGLBackingStore(QWindow *window); + virtual ~QBBGLBackingStore(); + + virtual QPaintDevice *paintDevice() { return m_paintDevice; } + virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + virtual void resize(const QSize &size, const QRegion &staticContents); + virtual void beginPaint(const QRegion ®ion); + virtual void endPaint(const QRegion ®ion); + + void resizeSurface(const QSize &size); + +private: + QOpenGLContext *m_openGLContext; + QBBGLPaintDevice *m_paintDevice; + QSize m_requestedSize; + QSize m_size; +}; + +QT_END_NAMESPACE + +#endif // QBBGLBACKINGSTORE_H diff --git a/src/plugins/platforms/blackberry/qbbglcontext.cpp b/src/plugins/platforms/blackberry/qbbglcontext.cpp new file mode 100644 index 0000000000..fb74fdb5d2 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglcontext.cpp @@ -0,0 +1,356 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbglcontext.h" +#include "qbbrootwindow.h" +#include "qbbscreen.h" +#include "qbbwindow.h" + +#include "private/qeglconvenience_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +EGLDisplay QBBGLContext::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; +} + +QBBGLContext::QBBGLContext(QOpenGLContext *glContext) + : QPlatformOpenGLContext(), + m_glContext(glContext), + m_eglSurface(EGL_NO_SURFACE) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QSurfaceFormat format = m_glContext->format(); + + // Set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set EGL API, err=%d", eglGetError()); + } + + // Get colour channel sizes from window format + int alphaSize = format.alphaBufferSize(); + int redSize = format.redBufferSize(); + int greenSize = format.greenBufferSize(); + int blueSize = format.blueBufferSize(); + + // Check if all channels are don't care + if (alphaSize == -1 && redSize == -1 && greenSize == -1 && blueSize == -1) { + // Set colour channels based on depth of window's screen + QBBScreen *screen = static_cast(QBBScreen::screens().first()); + int depth = screen->depth(); + if (depth == 32) { + // SCREEN_FORMAT_RGBA8888 + alphaSize = 8; + redSize = 8; + greenSize = 8; + blueSize = 8; + } else { + // SCREEN_FORMAT_RGB565 + alphaSize = 0; + redSize = 5; + greenSize = 6; + blueSize = 5; + } + } else { + // Choose best match based on supported pixel formats + if (alphaSize <= 0 && redSize <= 5 && greenSize <= 6 && blueSize <= 5) { + // SCREEN_FORMAT_RGB565 + alphaSize = 0; + redSize = 5; + greenSize = 6; + blueSize = 5; + } else { + // SCREEN_FORMAT_RGBA8888 + alphaSize = 8; + redSize = 8; + greenSize = 8; + blueSize = 8; + } + } + + // Update colour channel sizes in window format + format.setAlphaBufferSize(alphaSize); + format.setRedBufferSize(redSize); + format.setGreenBufferSize(greenSize); + format.setBlueBufferSize(blueSize); + format.setSamples(2); + + // Select EGL config based on requested window format + m_eglConfig = q_configFromGLFormat(ms_eglDisplay, format); + if (m_eglConfig == 0) { + qFatal("QBB: failed to find EGL config"); + } + + m_eglContext = eglCreateContext(ms_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs()); + if (m_eglContext == EGL_NO_CONTEXT) { + checkEGLError("eglCreateContext"); + qFatal("QBB: failed to create EGL context, err=%d", eglGetError()); + } + + // Query/cache window format of selected EGL config + m_windowFormat = q_glFormatFromConfig(ms_eglDisplay, m_eglConfig); +} + +QBBGLContext::~QBBGLContext() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + // Cleanup EGL context if it exists + if (m_eglContext != EGL_NO_CONTEXT) { + eglDestroyContext(ms_eglDisplay, m_eglContext); + } + + // Cleanup EGL surface if it exists + destroySurface(); +} + +void QBBGLContext::initialize() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Initialize connection to EGL + ms_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (ms_eglDisplay == EGL_NO_DISPLAY) { + checkEGLError("eglGetDisplay"); + qFatal("QBB: failed to obtain EGL display"); + } + + EGLBoolean eglResult = eglInitialize(ms_eglDisplay, 0, 0); + if (eglResult != EGL_TRUE) { + checkEGLError("eglInitialize"); + qFatal("QBB: failed to initialize EGL display, err=%d", eglGetError()); + } +} + +void QBBGLContext::shutdown() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Close connection to EGL + eglTerminate(ms_eglDisplay); +} + +bool QBBGLContext::makeCurrent(QPlatformSurface *surface) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set EGL API, err=%d", eglGetError()); + } + + if (m_eglSurface == EGL_NO_SURFACE) + createSurface(surface); + + eglResult = eglMakeCurrent(ms_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + if (eglResult != EGL_TRUE) { + checkEGLError("eglMakeCurrent"); + qFatal("QBB: failed to set current EGL context, err=%d", eglGetError()); + } + return (eglResult == EGL_TRUE); +} + +void QBBGLContext::doneCurrent() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set EGL API, err=%d", eglGetError()); + } + + // clear curent EGL context and unbind EGL surface + eglResult = eglMakeCurrent(ms_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to clear current EGL context, err=%d", eglGetError()); + } +} + +void QBBGLContext::swapBuffers(QPlatformSurface *surface) +{ + Q_UNUSED(surface); +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + // Set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set EGL API, err=%d", eglGetError()); + } + + // Post EGL surface to window + eglResult = eglSwapBuffers(ms_eglDisplay, m_eglSurface); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to swap EGL buffers, err=%d", eglGetError()); + } +} + +QFunctionPointer QBBGLContext::getProcAddress(const QByteArray &procName) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + // Set current rendering API + EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set EGL API, err=%d", eglGetError()); + } + + // Lookup EGL extension function pointer + return static_cast(eglGetProcAddress(procName.constData())); +} + +EGLint *QBBGLContext::contextAttrs() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + // Choose EGL settings based on OpenGL version +#if defined(QT_OPENGL_ES_2) + static EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + return attrs; +#else + return 0; +#endif +} + +bool QBBGLContext::isCurrent() const +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + return (eglGetCurrentContext() == m_eglContext); +} + +void QBBGLContext::createSurface(QPlatformSurface *surface) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Get a pointer to the corresponding platform window + QBBWindow *platformWindow = dynamic_cast(surface); + if (!platformWindow) { + qFatal("QBB: unable to create EGLSurface without a QBBWindow"); + } + + // If the platform window does not yet have any buffers, we create + // a temporary set of buffers with a size of 1x1 pixels. This will + // suffice until such time as the platform window has obtained + // buffers of the proper size + if (!platformWindow->hasBuffers()) { + platformWindow->setPlatformOpenGLContext(this); + m_surfaceSize = platformWindow->geometry().size(); + platformWindow->setBufferSize(m_surfaceSize); + } + + // 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("QBB: failed to create EGL surface, err=%d", eglGetError()); + } +} + +void QBBGLContext::destroySurface() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + // Destroy EGL surface if it exists + if (m_eglSurface != EGL_NO_SURFACE) { + EGLBoolean eglResult = eglDestroySurface(ms_eglDisplay, m_eglSurface); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to destroy EGL surface, err=%d", eglGetError()); + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbglcontext.h b/src/plugins/platforms/blackberry/qbbglcontext.h new file mode 100644 index 0000000000..8ea1df5f40 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglcontext.h @@ -0,0 +1,93 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBGLCONTEXT_H +#define QBBGLCONTEXT_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBGLContext : public QPlatformOpenGLContext +{ +public: + QBBGLContext(QOpenGLContext *glContext); + virtual ~QBBGLContext(); + + static void initialize(); + static void shutdown(); + + virtual bool makeCurrent(QPlatformSurface *surface); + virtual void doneCurrent(); + virtual void swapBuffers(QPlatformSurface *surface); + virtual QFunctionPointer getProcAddress(const QByteArray &procName); + + virtual QSurfaceFormat format() const { return m_windowFormat; } + + bool isCurrent() const; + + void createSurface(QPlatformSurface *surface); + void destroySurface(); + +private: + /** \todo Should this be non-static so we can use additional displays? */ + static EGLDisplay ms_eglDisplay; + + QSurfaceFormat m_windowFormat; + QOpenGLContext *m_glContext; + + EGLConfig m_eglConfig; + EGLContext m_eglContext; + EGLSurface m_eglSurface; + QSize m_surfaceSize; + + static EGLint *contextAttrs(); +}; + +QT_END_NAMESPACE + +#endif // QBBGLCONTEXT_H diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp b/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp new file mode 100644 index 0000000000..fab3d3f151 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp @@ -0,0 +1,1696 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbinputcontext_imf.h" +#include "qbbeventthread.h" +#include "qbbvirtualkeyboard.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "imf/imf_client.h" +#include "imf/input_control.h" +#include +#include + +/** TODO: + Support inputMethodHints to restrict input (needs additional features in IMF). +*/ + +#define STRX(x) #x +#define STR(x) STRX(x) + +// Someone tell me why input_control methods are in this namespace, but the rest is not. +using namespace InputMethodSystem; + +#define qs(x) QString::fromLatin1(x) +#define iarg(name) event->mArgs[qs(#name)] = QVariant::fromValue(name) +#define parg(name) event->mArgs[qs(#name)] = QVariant::fromValue((void*)name) +namespace +{ + +spannable_string_t *toSpannableString(const QString &text); +static const input_session_t *sInputSession = 0; +bool isSessionOkay(input_session_t *ic) +{ + return ic !=0 && sInputSession != 0 && ic->component_id == sInputSession->component_id; +} + +enum ImfEventType +{ + ImfBeginBatchEdit, + ImfClearMetaKeyStates, + ImfCommitText, + ImfDeleteSurroundingText, + ImfEndBatchEdit, + ImfFinishComposingText, + ImfGetCursorCapsMode, + ImfGetCursorPosition, + ImfGetExtractedText, + ImfGetSelectedText, + ImfGetTextAfterCursor, + ImfGetTextBeforeCursor, + ImfPerformEditorAction, + ImfReportFullscreenMode, + ImfSendEvent, + ImfSendAsyncEvent, + ImfSetComposingRegion, + ImfSetComposingText, + ImfSetSelection +}; + +// We use this class as a round about way to support a posting synchronous event into +// Qt's main thread from the IMF thread. +class ImfEventResult +{ +public: + ImfEventResult() + { + m_mutex.lock(); + } + + ~ImfEventResult() + { + m_mutex.unlock(); + } + + void wait() + { + m_wait.wait(&m_mutex); + } + + void signal() + { + m_wait.wakeAll(); + } + + void setResult(const QVariant& result) + { + m_mutex.lock(); + m_retVal = result; + signal(); + m_mutex.unlock(); + } + + QVariant result() + { + return m_retVal; + } + +private: + QVariant m_retVal; + QMutex m_mutex; + QWaitCondition m_wait; +}; + +class ImfEvent : public QEvent +{ + public: + ImfEvent(input_session_t *session, ImfEventType type, ImfEventResult *result) : + QEvent((QEvent::Type)sUserEventType), + m_session(session), + m_imfType(type), + m_result(result) + { + } + ~ImfEvent() { } + + input_session_t *m_session; + ImfEventType m_imfType; + QVariantHash m_args; + ImfEventResult *m_result; + + static int sUserEventType; +}; +int ImfEvent::sUserEventType = QEvent::registerEventType(); + +static int32_t imfBeginBatchEdit(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfBeginBatchEdit, &result); + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfClearMetaKeyStates(input_session_t *ic, int32_t states) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfClearMetaKeyStates, &result); + iarg(states); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfCommitText, &result); + parg(text); + iarg(new_cursor_position); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfDeleteSurroundingText, &result); + iarg(left_length); + iarg(right_length); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfEndBatchEdit(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfEndBatchEdit, &result); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfFinishComposingText(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfFinishComposingText, &result); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static int32_t imfGetCursorCapsMode(input_session_t *ic, int32_t req_modes) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetCursorCapsMode, &result); + iarg(req_modes); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + int32_t ret = result.result().value(); + return ret; +} + +static int32_t imfGetCursorPosition(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetCursorPosition, &result); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + + return ret; +} + +static extracted_text_t *imfGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) { + extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1); + et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1); + return et; + } + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetExtractedText, &result); + parg(request); + iarg(flags); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + return result.result().value(); +} + +static spannable_string_t *imfGetSelectedText(input_session_t *ic, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetSelectedText, &result); + iarg(flags); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + return result.result().value(); +} + +static spannable_string_t *imfGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetTextAfterCursor, &result); + iarg(n); + iarg(flags); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + return result.result().value(); +} + +static spannable_string_t *imfGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfGetTextBeforeCursor, &result); + iarg(n); + iarg(flags); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + return result.result().value(); +} + +static int32_t imfPerformEditorAction(input_session_t *ic, int32_t editor_action) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfPerformEditorAction, &result); + iarg(editor_action); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + return ret; +} + +static int32_t imfReportFullscreenMode(input_session_t *ic, int32_t enabled) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfReportFullscreenMode, &result); + iarg(enabled); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + return ret; +} + +static int32_t imfSendEvent(input_session_t *ic, event_t *event) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEvent *imfEvent = new ImfEvent(ic, ImfSendEvent, 0); + imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast(event)); + + QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent); + + return 0; +} + +static int32_t imfSendAsyncEvent(input_session_t *ic, event_t *event) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEvent *imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0); + imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast(event)); + + QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent); + + return 0; +} + +static int32_t imfSetComposingRegion(input_session_t *ic, int32_t start, int32_t end) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfSetComposingRegion, &result); + iarg(start); + iarg(end); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + return ret; +} + +static int32_t imfSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfSetComposingText, &result); + parg(text); + iarg(new_cursor_position); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + return ret; +} + +static int32_t imfSetSelection(input_session_t *ic, int32_t start, int32_t end) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEventResult result; + ImfEvent *event = new ImfEvent(ic, ImfSetSelection, &result); + iarg(start); + iarg(end); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + int32_t ret = result.result().value(); + return ret; +} + +static connection_interface_t ic_funcs = { + imfBeginBatchEdit, + imfClearMetaKeyStates, + imfCommitText, + imfDeleteSurroundingText, + imfEndBatchEdit, + imfFinishComposingText, + imfGetCursorCapsMode, + imfGetCursorPosition, + imfGetExtractedText, + imfGetSelectedText, + imfGetTextAfterCursor, + imfGetTextBeforeCursor, + imfPerformEditorAction, + imfReportFullscreenMode, + NULL, //ic_send_key_event + imfSendEvent, + imfSendAsyncEvent, + imfSetComposingRegion, + imfSetComposingText, + imfSetSelection, + NULL, //ic_set_candidates, +}; + +static void +initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId) +{ + static int s_transactionId; + + // Make sure structure is squeaky clean since it's not clear just what is significant. + memset(pEvent, 0, sizeof(event_t)); + pEvent->event_type = eventType; + pEvent->event_id = eventId; + pEvent->pid = getpid(); + pEvent->component_id = pSession->component_id; + pEvent->transaction_id = ++s_transactionId; +} + +spannable_string_t *toSpannableString(const QString &text) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << text; +#endif + + spannable_string_t *pString = reinterpret_cast(malloc(sizeof(spannable_string_t))); + pString->str = (wchar_t *)malloc(sizeof(wchar_t) * text.length() + 1); + pString->length = text.length(); + pString->spans = NULL; + pString->spans_count = 0; + + const QChar *pData = text.constData(); + wchar_t *pDst = pString->str; + + while (!pData->isNull()) + { + *pDst = pData->unicode(); + pDst++; + pData++; + } + *pDst = 0; + + return pString; +} + +} // namespace + +static const input_session_t *(*p_ictrl_open_session)(connection_interface_t *) = 0; +static void (*p_ictrl_close_session)(input_session_t *) = 0; +static int32_t (*p_ictrl_dispatch_event)(event_t*) = 0; +static int32_t (*p_imf_client_init)() = 0; +static void (*p_imf_client_disconnect)() = 0; +static int32_t (*p_vkb_init_selection_service)() = 0; +static int32_t (*p_ictrl_get_num_active_sessions)() = 0; +static bool s_imfInitFailed = false; + +static bool imfAvailable() +{ + static bool s_imfDisabled = getenv("DISABLE_IMF") != NULL; + static bool s_imfReady = false; + + if ( s_imfInitFailed || s_imfDisabled) { + return false; + } + else if ( s_imfReady ) { + return true; + } + + if ( p_imf_client_init == NULL ) { + void *handle = dlopen("libinput_client.so.1", 0); + if ( handle ) { + p_imf_client_init = (int32_t (*)()) dlsym(handle, "imf_client_init"); + p_imf_client_disconnect = (void (*)()) dlsym(handle, "imf_client_disconnect"); + p_ictrl_open_session = (const input_session_t *(*)(connection_interface_t *))dlsym(handle, "ictrl_open_session"); + p_ictrl_close_session = (void (*)(input_session_t *))dlsym(handle, "ictrl_close_session"); + p_ictrl_dispatch_event = (int32_t (*)(event_t *))dlsym(handle, "ictrl_dispatch_event"); + p_vkb_init_selection_service = (int32_t (*)())dlsym(handle, "vkb_init_selection_service"); + p_ictrl_get_num_active_sessions = (int32_t (*)())dlsym(handle, "ictrl_get_num_active_sessions"); + } + else + { + qCritical() << Q_FUNC_INFO << "libinput_client.so.1 is not present - IMF services are disabled."; + s_imfDisabled = true; + return false; + } + if ( p_imf_client_init && p_ictrl_open_session && p_ictrl_dispatch_event ) { + s_imfReady = true; + } + else { + p_ictrl_open_session = NULL; + p_ictrl_dispatch_event = NULL; + s_imfDisabled = true; + qCritical() << Q_FUNC_INFO << "libinput_client.so.1 did not contain the correct symbols, library mismatch? IMF services are disabled."; + return false; + } + } + + return s_imfReady; +} + +QBBInputContext::QBBInputContext(): + QPlatformInputContext(), + m_lastCaretPos(0), + m_isComposing(false), + m_inputPanelVisible(false), + m_inputPanelLocale(QLocale::c()) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!imfAvailable()) + return; + + if ( p_imf_client_init() != 0 ) { + s_imfInitFailed = true; + qCritical("imf_client_init failed - IMF services will be unavailable"); + } + + QCoreApplication::instance()->installEventFilter(this); + + // p_vkb_init_selection_service(); + + QBBVirtualKeyboard &keyboard = QBBVirtualKeyboard::instance(); + connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool))); + connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale))); + keyboardVisibilityChanged(keyboard.isVisible()); + keyboardLocaleChanged(keyboard.locale()); + + QInputMethod *inputMethod = qApp->inputMethod(); + connect(inputMethod, SIGNAL(inputItemChanged()), this, SLOT(inputItemChanged())); + +} + +QBBInputContext::~QBBInputContext() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!imfAvailable()) + return; + + QCoreApplication::instance()->removeEventFilter(this); + p_imf_client_disconnect(); +} + +#define getarg(type, name) type name = imfEvent->mArgs[qs(#name)].value() +#define getparg(type, name) type name = (type)(imfEvent->mArgs[qs(#name)].value()) + +bool QBBInputContext::isValid() const +{ + return imfAvailable(); +} + +bool QBBInputContext::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == ImfEvent::sUserEventType) { + // Forward the event to our real handler. + ImfEvent *imfEvent = static_cast(event); + switch (imfEvent->m_imfType) { + case ImfBeginBatchEdit: { + int32_t ret = onBeginBatchEdit(imfEvent->m_session); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfClearMetaKeyStates: { + getarg(int32_t, states); + int32_t ret = onClearMetaKeyStates(imfEvent->m_session, states); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfCommitText: { + getparg(spannable_string_t*, text); + getarg(int32_t, new_cursor_position); + int32_t ret = onCommitText(imfEvent->m_session, text, new_cursor_position); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfDeleteSurroundingText: { + getarg(int32_t, left_length); + getarg(int32_t, right_length); + int32_t ret = onDeleteSurroundingText(imfEvent->m_session, left_length, right_length); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfEndBatchEdit: { + int32_t ret = onEndBatchEdit(imfEvent->m_session); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfFinishComposingText: { + int32_t ret = onFinishComposingText(imfEvent->m_session); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetCursorCapsMode: { + getarg(int32_t, req_modes); + int32_t ret = onGetCursorCapsMode(imfEvent->m_session, req_modes); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetCursorPosition: { + int32_t ret = onGetCursorPosition(imfEvent->m_session); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetExtractedText: { + getparg(extracted_text_request_t*, request); + getarg(int32_t, flags); + extracted_text_t *ret = onGetExtractedText(imfEvent->m_session, request, flags); + imfEvent->m_result->setResult(QVariant::fromValue(static_cast(ret))); + break; + } + + case ImfGetSelectedText: { + getarg(int32_t, flags); + spannable_string_t *ret = onGetSelectedText(imfEvent->m_session, flags); + imfEvent->m_result->setResult(QVariant::fromValue(static_cast(ret))); + break; + } + + case ImfGetTextAfterCursor: { + getarg(int32_t, n); + getarg(int32_t, flags); + spannable_string_t *ret = onGetTextAfterCursor(imfEvent->m_session, n, flags); + imfEvent->m_result->setResult(QVariant::fromValue(static_cast(ret))); + break; + } + + case ImfGetTextBeforeCursor: { + getarg(int32_t, n); + getarg(int32_t, flags); + spannable_string_t *ret = onGetTextBeforeCursor(imfEvent->m_session, n, flags); + imfEvent->m_result->setResult(QVariant::fromValue((void*)ret)); + break; + } + + case ImfPerformEditorAction: { + getarg(int32_t, editor_action); + int32_t ret = onPerformEditorAction(imfEvent->m_session, editor_action); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfReportFullscreenMode: { + getarg(int32_t, enabled); + int32_t ret = onReportFullscreenMode(imfEvent->m_session, enabled); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSendEvent: { + getparg(event_t*, event); + onSendEvent(imfEvent->m_session, event); + break; + } + + case ImfSendAsyncEvent: { + getparg(event_t*, event); + onSendAsyncEvent(imfEvent->m_session, event); + break; + } + + case ImfSetComposingRegion: { + getarg(int32_t, start); + getarg(int32_t, end); + int32_t ret = onSetComposingRegion(imfEvent->m_session, start, end); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSetComposingText: { + getparg(spannable_string_t*, text); + getarg(int32_t, new_cursor_position); + int32_t ret = onSetComposingText(imfEvent->m_session, text, new_cursor_position); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSetSelection: { + getarg(int32_t, start); + getarg(int32_t, end); + int32_t ret = onSetSelection(imfEvent->m_session, start, end); + imfEvent->m_result->setResult(QVariant::fromValue(ret)); + break; + } + }; //switch + + return true; + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } +} + +bool QBBInputContext::filterEvent( const QEvent *event ) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << event; +#endif + switch (event->type()) { + case QEvent::CloseSoftwareInputPanel: { + return dispatchCloseSoftwareInputPanel(); + } + case QEvent::RequestSoftwareInputPanel: { + return dispatchRequestSoftwareInputPanel(); + } + default: + return false; + } +} + +void QBBInputContext::reset() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + endComposition(); +} + +void QBBInputContext::update(Qt::InputMethodQueries queries) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + reset(); + + QPlatformInputContext::update(queries); +} + +void QBBInputContext::closeSession() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO +#endif + if (!imfAvailable()) + return; + + if (sInputSession) { + p_ictrl_close_session((input_session_t *)sInputSession); + sInputSession = 0; + } +} + +void QBBInputContext::openSession() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO +#endif + if (!imfAvailable()) + return; + + closeSession(); + sInputSession = p_ictrl_open_session(&ic_funcs); +} + +bool QBBInputContext::hasSession() +{ + return sInputSession != 0; +} + +bool QBBInputContext::hasSelectedText() +{ + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!input) + return false; + + QInputMethodQueryEvent query(Qt::ImCurrentSelection); + QCoreApplication::sendEvent(input, &query); + + return !query.value(Qt::ImCurrentSelection).toString().isEmpty(); +} + +bool QBBInputContext::dispatchRequestSoftwareInputPanel() +{ + QBBVirtualKeyboard::instance().showKeyboard(); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << "QBB: requesting virtual keyboard"; +#endif + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return true; + + if (!hasSession()) + openSession(); + + // This also means that the caret position has moved + QInputMethodQueryEvent query(Qt::ImCursorPosition); + QCoreApplication::sendEvent(input, &query); + int caretPos = query.value(Qt::ImCursorPosition).toInt(); + caret_event_t caretEvent; + memset(&caretEvent, 0, sizeof(caret_event_t)); + initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED); + caretEvent.old_pos = m_lastCaretPos; + m_lastCaretPos = caretEvent.new_pos = caretPos; + p_ictrl_dispatch_event((event_t *)&caretEvent); + return true; +} + +bool QBBInputContext::dispatchCloseSoftwareInputPanel() +{ + QBBVirtualKeyboard::instance().hideKeyboard(); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << "QBB: hiding virtual keyboard"; +#endif + + // This also means we are stopping composition, but we should already have done that. + return true; +} + +/** + * IMF Event Dispatchers. + */ +bool QBBInputContext::dispatchFocusEvent(FocusEventId id, int hints) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!sInputSession) { + qWarning() << Q_FUNC_INFO << "Attempt to dispatch a focus event with no input session."; + return false; + } + + if (!imfAvailable()) + return false; + + // Set the last caret position to 0 since we don't really have one and we don't + // want to have the old one. + m_lastCaretPos = 0; + + focus_event_t focusEvent; + memset(&focusEvent, 0, sizeof(focusEvent)); + initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, id); + focusEvent.style = DEFAULT_STYLE; + + if (hints && Qt::ImhNoPredictiveText) + focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION; + if (hints && Qt::ImhNoAutoUppercase) + focusEvent.style |= NO_AUTO_TEXT; + + p_ictrl_dispatch_event((event_t *)&focusEvent); + + return true; +} + +bool QBBInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap) +{ + if (!imfAvailable()) + return false; + + int key = (flags & KEY_SYM_VALID) ? sym : cap; + bool navKey = false; + switch ( key ) { + case KEYCODE_RETURN: + /* In a single line edit we should end composition because enter might be used by something. + endComposition(); + return false;*/ + break; + + case KEYCODE_BACKSPACE: + case KEYCODE_DELETE: + // If there is a selection range, then we want a delete key to operate on that (by + // deleting the contents of the select range) rather than operating on the composition + // range. + if (hasSelectedText()) + return false; + break; + case KEYCODE_LEFT: + key = NAVIGATE_LEFT; + navKey = true; + break; + case KEYCODE_RIGHT: + key = NAVIGATE_RIGHT; + navKey = true; + break; + case KEYCODE_UP: + key = NAVIGATE_UP; + navKey = true; + break; + case KEYCODE_DOWN: + key = NAVIGATE_DOWN; + navKey = true; + break; + case KEYCODE_CAPS_LOCK: + case KEYCODE_LEFT_SHIFT: + case KEYCODE_RIGHT_SHIFT: + case KEYCODE_LEFT_CTRL: + case KEYCODE_RIGHT_CTRL: + case KEYCODE_LEFT_ALT: + case KEYCODE_RIGHT_ALT: + case KEYCODE_MENU: + case KEYCODE_LEFT_HYPER: + case KEYCODE_RIGHT_HYPER: + case KEYCODE_INSERT: + case KEYCODE_HOME: + case KEYCODE_PG_UP: + case KEYCODE_END: + case KEYCODE_PG_DOWN: + // Don't send these + key = 0; + break; + } + + if ( mod & KEYMOD_CTRL ) { + // If CTRL is pressed, just let AIR handle it. But terminate any composition first + //endComposition(); + return false; + } + + // Pass the keys we don't know about on through + if ( key == 0 ) + return false; + + // IMF doesn't need key releases so just swallow them. + if (!(flags & KEY_DOWN)) + return true; + + if ( navKey ) { + // Even if we're forwarding up events, we can't do this for + // navigation keys. + if ( flags & KEY_DOWN ) { + navigation_event_t navEvent; + initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key); + navEvent.magnitude = 1; +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "dispatch navigation event " << key; +#endif + p_ictrl_dispatch_event(&navEvent.event); + } + } + else { + key_event_t keyEvent; + initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP); + keyEvent.key_code = key; + keyEvent.character = 0; + keyEvent.meta_key_state = 0; + + p_ictrl_dispatch_event(&keyEvent.event); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "dispatch key event " << key; +#endif + } + + scan = 0; + return true; +} + +void QBBInputContext::endComposition() +{ + if (!m_isComposing) + return; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return; + + QList attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(m_composingText); + m_composingText = QString(); + m_isComposing = false; + QCoreApplication::sendEvent(input, &event); + + action_event_t actionEvent; + memset(&actionEvent, 0, sizeof(actionEvent)); + initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION); + p_ictrl_dispatch_event(&actionEvent.event); +} + +void QBBInputContext::setComposingText(QString const& composingText) +{ + m_composingText = composingText; + m_isComposing = true; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return; + + QList attributes; + QTextCharFormat format; + format.setFontUnderline(true); + attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format)); + + QInputMethodEvent event(composingText, attributes); + + QCoreApplication::sendEvent(input, &event); +} + +int32_t QBBInputContext::processEvent(event_t *event) +{ + int32_t result = -1; + switch (event->event_type) { + case EVENT_SPELL_CHECK: { + #if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "EVENT_SPELL_CHECK"; + #endif + result = 0; + break; + } + + case EVENT_NAVIGATION: { + #if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "EVENT_NAVIGATION"; + #endif + + int key = event->event_id == NAVIGATE_UP ? KEYCODE_UP : + event->event_id == NAVIGATE_DOWN ? KEYCODE_DOWN : + event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT : + event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0; + + QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0); + QBBEventThread::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0); + result = 0; + break; + } + + case EVENT_KEY: { + #if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "EVENT_KEY"; + #endif + key_event_t *kevent = static_cast(event); + + QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code); + QBBEventThread::injectKeyboardEvent(KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code); + + result = 0; + break; + } + + case EVENT_ACTION: + // Don't care, indicates that IMF is done. + break; + + case EVENT_CARET: + case EVENT_NOTHING: + case EVENT_FOCUS: + case EVENT_USER_ACTION: + case EVENT_STROKE: + case EVENT_INVOKE_LATER: + qCritical() << Q_FUNC_INFO << "Unsupported event type: " << event->event_type; + break; + default: + qCritical() << Q_FUNC_INFO << "Unknown event type: " << event->event_type; + } + return result; +} + +/** + * IMF Event Handlers + */ + +int32_t QBBInputContext::onBeginBatchEdit(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // We don't care. + return 0; +} + +int32_t QBBInputContext::onClearMetaKeyStates(input_session_t *ic, int32_t states) +{ + Q_UNUSED(states); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << Q_FUNC_INFO << "onClearMetaKeyStates is unsupported."; + return 0; +} + +int32_t QBBInputContext::onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +{ + Q_UNUSED(new_cursor_position); // TODO: How can we set the cursor position it's not part of the API. + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + QString commitString = QString::fromWCharArray(text->str, text->length); + +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "Committing [" << commitString << "]"; +#endif + + QList attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(commitString, 0, 0); + + QCoreApplication::sendEvent(input, &event); + m_composingText = QString(); + + return 0; +} + +int32_t QBBInputContext::onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "L:" << left_length << " R:" << right_length; +#endif + + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + if (hasSelectedText()) { + QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0); + QBBEventThread::injectKeyboardEvent(KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0); + reset(); + return 0; + } + + int replacementLength = left_length + right_length; + int replacementStart = -left_length; + + QList attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(QLatin1String(""), replacementStart, replacementLength); + QCoreApplication::sendEvent(input, &event); + + return 0; +} + +int32_t QBBInputContext::onEndBatchEdit(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + return 0; +} + +int32_t QBBInputContext::onFinishComposingText(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + // Only update the control, no need to send a message back to imf (don't call + // end composition) + QList attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(m_composingText); + m_composingText = QString(); + m_isComposing = false; + QCoreApplication::sendEvent(input, &event); + + return 0; +} + +int32_t QBBInputContext::onGetCursorCapsMode(input_session_t *ic, int32_t req_modes) +{ + Q_UNUSED(req_modes); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << Q_FUNC_INFO << "onGetCursorCapsMode is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onGetCursorPosition(input_session_t *ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + QInputMethodQueryEvent query(Qt::ImCursorPosition); + QCoreApplication::sendEvent(input, &query); + m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + + return m_lastCaretPos; +} + +extracted_text_t *QBBInputContext::onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags) +{ + Q_UNUSED(flags); + Q_UNUSED(request); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) { + extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1); + et->text = reinterpret_cast(calloc(sizeof(spannable_string_t),1)); + return et; + } + + // Used to update dictionaries, but not supported right now. + extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1); + et->text = reinterpret_cast(calloc(sizeof(spannable_string_t),1)); + + return et; +} + +spannable_string_t *QBBInputContext::onGetSelectedText(input_session_t *ic, int32_t flags) +{ + Q_UNUSED(flags); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + QInputMethodQueryEvent query(Qt::ImCurrentSelection); + QCoreApplication::sendEvent(input, &query); + QString text = query.value(Qt::ImCurrentSelection).toString(); + + return toSpannableString(text); +} + +spannable_string_t *QBBInputContext::onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags) +{ + Q_UNUSED(flags); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return toSpannableString(""); + + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); + QCoreApplication::sendEvent(input, &query); + QString text = query.value(Qt::ImSurroundingText).toString(); + m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + + return toSpannableString(text.mid(m_lastCaretPos+1, n)); +} + +spannable_string_t *QBBInputContext::onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags) +{ + Q_UNUSED(flags); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return toSpannableString(""); + + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); + QCoreApplication::sendEvent(input, &query); + QString text = query.value(Qt::ImSurroundingText).toString(); + m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + + if (n < m_lastCaretPos) { + return toSpannableString(text.mid(m_lastCaretPos - n, n)); + } else { + return toSpannableString(text.mid(0, m_lastCaretPos)); + } +} + +int32_t QBBInputContext::onPerformEditorAction(input_session_t *ic, int32_t editor_action) +{ + Q_UNUSED(editor_action); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << Q_FUNC_INFO << "onPerformEditorAction is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onReportFullscreenMode(input_session_t *ic, int32_t enabled) +{ + Q_UNUSED(enabled); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << Q_FUNC_INFO << "onReportFullscreenMode is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onSendEvent(input_session_t *ic, event_t *event) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + return processEvent(event); +} + +int32_t QBBInputContext::onSendAsyncEvent(input_session_t *ic, event_t *event) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + return processEvent(event); +} + +int32_t QBBInputContext::onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); + QCoreApplication::sendEvent(input, &query); + QString text = query.value(Qt::ImSurroundingText).toString(); + m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + + QString empty = QString::fromLatin1(""); + text = text.mid(start, end - start); + + // Delete the current text. + QList attributes; + QInputMethodEvent event(empty, attributes); + event.setCommitString(empty, start - m_lastCaretPos, end - start); + QCoreApplication::sendEvent(input, &event); + + // Move the specified text into a preedit string. + setComposingText(text); + + return 0; +} + +int32_t QBBInputContext::onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +{ + Q_UNUSED(new_cursor_position); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + QInputPanel *panel = qApp->inputPanel(); + QObject *input = panel->inputItem(); + if (!imfAvailable() || !input) + return 0; + + m_isComposing = true; + + QString preeditString = QString::fromWCharArray(text->str, text->length); + setComposingText(preeditString); + + return 0; +} + +int32_t QBBInputContext::onSetSelection(input_session_t *ic, int32_t start, int32_t end) +{ + Q_UNUSED(start); + Q_UNUSED(end); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << Q_FUNC_INFO << "onSetSelection is unsupported."; + + return 0; +} + +void QBBInputContext::showInputPanel() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + dispatchRequestSoftwareInputPanel(); +} + +void QBBInputContext::hideInputPanel() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + dispatchCloseSoftwareInputPanel(); +} + +bool QBBInputContext::isInputPanelVisible() const +{ + return m_inputPanelVisible; +} + +QLocale QBBInputContext::locale() const +{ + return m_inputPanelLocale; +} + +void QBBInputContext::keyboardVisibilityChanged(bool visible) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "visible=" << visible; +#endif + if (m_inputPanelVisible != visible) { + m_inputPanelVisible = visible; + emitInputPanelVisibleChanged(); + } +} + +void QBBInputContext::keyboardLocaleChanged(const QLocale &locale) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "locale=" << locale; +#endif + if (m_inputPanelLocale != locale) { + m_inputPanelLocale = locale; + emitLocaleChanged(); + } +} + +void QBBInputContext::inputItemChanged() +{ + QInputMethod *inputMethod = qApp->inputMethod(); + QObject *inputItem = inputMethod->inputItem(); + +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "input item=" << inputItem; +#endif + + if (!inputItem) { + if (m_inputPanelVisible) + hideInputPanel(); + } else { + if (qobject_cast(inputItem)) { + QBBVirtualKeyboard::instance().setKeyboardMode(QBBVirtualKeyboard::NumPunc); + } else { + QBBVirtualKeyboard::instance().setKeyboardMode(QBBVirtualKeyboard::Default); + } + if (!m_inputPanelVisible) + showInputPanel(); + } +} diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_imf.h b/src/plugins/platforms/blackberry/qbbinputcontext_imf.h new file mode 100644 index 0000000000..135ec02971 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_imf.h @@ -0,0 +1,132 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINPUTCONTEXT_H +#define QBBINPUTCONTEXT_H + +#include + +#include +#include +#include + +#include "imf/imf_client.h" +#include "imf/input_control.h" + +QT_BEGIN_NAMESPACE + +class QBBInputContext : public QPlatformInputContext +{ + Q_OBJECT +public: + QBBInputContext(); + ~QBBInputContext(); + + virtual bool isValid() const; + + virtual bool filterEvent(const QEvent *event); + virtual void reset(); + virtual void update(Qt::InputMethodQueries); + bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + + virtual void showInputPanel(); + virtual void hideInputPanel(); + virtual bool isInputPanelVisible() const; + + virtual QLocale locale() const; + +protected: + // Filters only for IMF events. + bool eventFilter(QObject *obj, QEvent *event); + +private Q_SLOTS: + void keyboardVisibilityChanged(bool visible); + void keyboardLocaleChanged(const QLocale &locale); + void inputItemChanged(); + +private: + // IMF Event dispatchers + bool dispatchFocusEvent(FocusEventId id, int hints = Qt::ImhNone); + bool dispatchRequestSoftwareInputPanel(); + bool dispatchCloseSoftwareInputPanel(); + int32_t processEvent(event_t *event); + + void closeSession(); + void openSession(); + bool hasSession(); + void endComposition(); + void setComposingText(QString const &composingText); + bool hasSelectedText(); + + // IMF Event handlers - these events will come in from QCoreApplication. + int32_t onBeginBatchEdit(input_session_t *ic); + int32_t onClearMetaKeyStates(input_session_t *ic, int32_t states); + int32_t onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position); + int32_t onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length); + int32_t onEndBatchEdit(input_session_t *ic); + int32_t onFinishComposingText(input_session_t *ic); + int32_t onGetCursorCapsMode(input_session_t *ic, int32_t req_modes); + int32_t onGetCursorPosition(input_session_t *ic); + extracted_text_t *onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags); + spannable_string_t *onGetSelectedText(input_session_t *ic, int32_t flags); + spannable_string_t *onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags); + spannable_string_t *onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags); + int32_t onPerformEditorAction(input_session_t *ic, int32_t editor_action); + int32_t onReportFullscreenMode(input_session_t *ic, int32_t enabled); + int32_t onSendEvent(input_session_t *ic, event_t *event); + int32_t onSendAsyncEvent(input_session_t *ic, event_t *event); + int32_t onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end); + int32_t onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position); + int32_t onSetSelection(input_session_t *ic, int32_t start, int32_t end); + int32_t onForceUpdate(); + + int m_lastCaretPos; + bool m_isComposing; + QString m_composingText; + bool m_inputPanelVisible; + QLocale m_inputPanelLocale; +}; + +Q_DECLARE_METATYPE(extracted_text_t*) + +QT_END_NAMESPACE + +#endif // QBBINPUTCONTEXT_H diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_noimf.cpp b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.cpp new file mode 100644 index 0000000000..abe84e2e53 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbinputcontext_noimf.h" +#include "qbbvirtualkeyboard.h" + +#include +#include +#include + +QBBInputContext::QBBInputContext() : + QPlatformInputContext(), + m_inputPanelVisible(false), + m_inputPanelLocale(QLocale::c()) +{ + QBBVirtualKeyboard &keyboard = QBBVirtualKeyboard::instance(); + connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool))); + connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale))); + keyboardVisibilityChanged(keyboard.isVisible()); + keyboardLocaleChanged(keyboard.locale()); + + QInputMethod *inputMethod = qApp->inputMethod(); + connect(inputMethod, SIGNAL(inputItemChanged()), this, SLOT(inputItemChanged())); +} + +QBBInputContext::~QBBInputContext() +{ +} + +bool QBBInputContext::isValid() const +{ + return true; +} + +bool QBBInputContext::hasPhysicalKeyboard() +{ + // TODO: This should query the system to check if a USB keyboard is connected. + return false; +} + +void QBBInputContext::reset() +{ +} + +bool QBBInputContext::filterEvent( const QEvent *event ) +{ + if (hasPhysicalKeyboard()) + return false; + + if (event->type() == QEvent::CloseSoftwareInputPanel) { + QBBVirtualKeyboard::instance().hideKeyboard(); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << "QBB: hiding virtual keyboard"; +#endif + return false; + } + + if (event->type() == QEvent::RequestSoftwareInputPanel) { + QBBVirtualKeyboard::instance().showKeyboard(); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << "QBB: requesting virtual keyboard"; +#endif + return false; + } + + return false; + +} + +bool QBBInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap) +{ + Q_UNUSED(flags); + Q_UNUSED(sym); + Q_UNUSED(mod); + Q_UNUSED(scan); + Q_UNUSED(cap); + return false; +} + +void QBBInputContext::showInputPanel() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QBBVirtualKeyboard::instance().showKeyboard(); +} + +void QBBInputContext::hideInputPanel() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QBBVirtualKeyboard::instance().hideKeyboard(); +} + +bool QBBInputContext::isInputPanelVisible() const +{ + return m_inputPanelVisible; +} + +QLocale QBBInputContext::locale() const +{ + return m_inputPanelLocale; +} + +void QBBInputContext::keyboardVisibilityChanged(bool visible) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "visible=" << visible; +#endif + if (m_inputPanelVisible != visible) { + m_inputPanelVisible = visible; + emitInputPanelVisibleChanged(); + } +} + +void QBBInputContext::keyboardLocaleChanged(const QLocale &locale) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "locale=" << locale; +#endif + if (m_inputPanelLocale != locale) { + m_inputPanelLocale = locale; + emitLocaleChanged(); + } +} + +void QBBInputContext::inputItemChanged() +{ + QInputMethod *inputMethod = qApp->inputMethod(); + QObject *inputItem = inputMethod->inputItem(); + +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << Q_FUNC_INFO << "input item=" << inputItem; +#endif + + if (!inputItem) { + if (m_inputPanelVisible) + hideInputPanel(); + } else { + if (qobject_cast(inputItem)) { + QBBVirtualKeyboard::instance().setKeyboardMode(QBBVirtualKeyboard::NumPunc); + } else { + QBBVirtualKeyboard::instance().setKeyboardMode(QBBVirtualKeyboard::Default); + } + if (!m_inputPanelVisible) + showInputPanel(); + } +} diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h new file mode 100644 index 0000000000..3d4d6da830 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINPUTCONTEXT_H +#define QBBINPUTCONTEXT_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QBBInputContext : public QPlatformInputContext +{ + Q_OBJECT +public: + explicit QBBInputContext(); + ~QBBInputContext(); + + virtual bool isValid() const; + + void reset(); + virtual bool filterEvent( const QEvent *event ); + bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + + virtual void showInputPanel(); + virtual void hideInputPanel(); + virtual bool isInputPanelVisible() const; + + virtual QLocale locale() const; + +private Q_SLOTS: + void keyboardVisibilityChanged(bool visible); + void keyboardLocaleChanged(const QLocale &locale); + void inputItemChanged(); + +private: + bool hasPhysicalKeyboard(); + + bool m_inputPanelVisible; + QLocale m_inputPanelLocale; +}; + +QT_END_NAMESPACE + +#endif // QBBINPUTCONTEXT_H diff --git a/src/plugins/platforms/blackberry/qbbintegration.cpp b/src/plugins/platforms/blackberry/qbbintegration.cpp new file mode 100644 index 0000000000..9f922a419e --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbintegration.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbintegration.h" +#include "qbbeventthread.h" +#include "qbbglbackingstore.h" +#include "qbbglcontext.h" +#include "qbbnavigatorthread.h" +#include "qbbrasterbackingstore.h" +#include "qbbscreen.h" +#include "qbbwindow.h" +#include "qbbvirtualkeyboard.h" +#include "qbbclipboard.h" +#include "qbbglcontext.h" + +#if defined(QBB_IMF) +#include "qbbinputcontext_imf.h" +#else +#include "qbbinputcontext_noimf.h" +#endif + +#include "private/qgenericunixfontdatabase_p.h" +#include "private/qgenericunixeventdispatcher_p.h" + +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QBBWindowMapper QBBIntegration::ms_windowMapper; +QMutex QBBIntegration::ms_windowMapperMutex; + +QBBIntegration::QBBIntegration() + : QPlatformIntegration() + , m_eventThread(0) + , m_navigatorThread(0) + , m_inputContext(0) + , m_fontDatabase(new QGenericUnixFontDatabase()) + , m_paintUsingOpenGL(false) + , m_eventDispatcher(createUnixEventDispatcher()) +#ifndef QT_NO_CLIPBOARD + , m_clipboard(0) +#endif +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Open connection to QNX composition manager + errno = 0; + int result = screen_create_context(&m_screenContext, SCREEN_APPLICATION_CONTEXT); + if (result != 0) { + qFatal("QBB: failed to connect to composition manager, errno=%d", errno); + } + + // Create displays for all possible screens (which may not be attached) + QBBScreen::createDisplays(m_screenContext); + Q_FOREACH (QPlatformScreen *screen, QBBScreen::screens()) { + screenAdded(screen); + } + + // Initialize global OpenGL resources + QBBGLContext::initialize(); + + // Create/start event thread + m_eventThread = new QBBEventThread(m_screenContext, *QBBScreen::primaryDisplay()); + m_eventThread->start(); + + // Create/start navigator thread + m_navigatorThread = new QBBNavigatorThread(*QBBScreen::primaryDisplay()); + m_navigatorThread->start(); + + // Create/start the keyboard class. + QBBVirtualKeyboard::instance(); + + // Set up the input context + m_inputContext = new QBBInputContext; +} + +QBBIntegration::~QBBIntegration() +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << "QBB: platform plugin shutdown begin"; +#endif + // Destroy the keyboard class. + QBBVirtualKeyboard::destroy(); + +#ifndef QT_NO_CLIPBOARD + // Delete the clipboard + delete m_clipboard; +#endif + + // Stop/destroy event thread + delete m_eventThread; + + // Stop/destroy navigator thread + delete m_navigatorThread; + + // Destroy all displays + QBBScreen::destroyDisplays(); + + // Close connection to QNX composition manager + screen_destroy_context(m_screenContext); + + // Cleanup global OpenGL resources + QBBGLContext::shutdown(); + +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << "QBB: platform plugin shutdown end"; +#endif +} + +bool QBBIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + switch (cap) { + case ThreadedPixmaps: return true; +#if defined(QT_OPENGL_ES) + case OpenGL: + return true; +#endif + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformWindow *QBBIntegration::createPlatformWindow(QWindow *window) const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // New windows are created on the primary display. + return new QBBWindow(window, m_screenContext); +} + +QPlatformBackingStore *QBBIntegration::createPlatformBackingStore(QWindow *window) const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + if (paintUsingOpenGL()) + return new QBBGLBackingStore(window); + else + return new QBBRasterBackingStore(window); +} + +QPlatformOpenGLContext *QBBIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + return new QBBGLContext(context); +} + +QPlatformInputContext *QBBIntegration::inputContext() const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + return m_inputContext; +} + +void QBBIntegration::moveToScreen(QWindow *window, int screen) +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << "QBBIntegration::moveToScreen - w=" << window << ", s=" << screen; +#endif + + // get platform window used by widget + QBBWindow *platformWindow = static_cast(window->handle()); + + // lookup platform screen by index + QBBScreen *platformScreen = static_cast(QBBScreen::screens().at(screen)); + + // move the platform window to the platform screen + platformWindow->setScreen(platformScreen); +} + +QList QBBIntegration::screens() const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + return QBBScreen::screens(); +} + +QAbstractEventDispatcher *QBBIntegration::guiThreadEventDispatcher() const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + return m_eventDispatcher; +} + +#ifndef QT_NO_CLIPBOARD +QPlatformClipboard *QBBIntegration::clipboard() const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + if (!m_clipboard) { + m_clipboard = new QBBClipboard; + } + return m_clipboard; +} +#endif + +QVariant QBBIntegration::styleHint(QPlatformIntegration::StyleHint hint) const +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + if (hint == ShowIsFullScreen) + return true; + + return QPlatformIntegration::styleHint(hint); +} + +QWindow *QBBIntegration::window(screen_window_t qnxWindow) +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QMutexLocker locker(&ms_windowMapperMutex); + Q_UNUSED(locker); + return ms_windowMapper.value(qnxWindow, 0); +} + +void QBBIntegration::addWindow(screen_window_t qnxWindow, QWindow *window) +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QMutexLocker locker(&ms_windowMapperMutex); + Q_UNUSED(locker); + ms_windowMapper.insert(qnxWindow, window); +} + +void QBBIntegration::removeWindow(screen_window_t qnxWindow) +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + QMutexLocker locker(&ms_windowMapperMutex); + Q_UNUSED(locker); + ms_windowMapper.remove(qnxWindow); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbintegration.h b/src/plugins/platforms/blackberry/qbbintegration.h new file mode 100644 index 0000000000..6b54329dac --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbintegration.h @@ -0,0 +1,119 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINTEGRATION_H +#define QBBINTEGRATION_H + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBEventThread; +class QBBInputContext; +class QBBNavigatorThread; +class QBBWindow; + +#ifndef QT_NO_CLIPBOARD +class QBBClipboard; +#endif + +template class QHash; +typedef QHash QBBWindowMapper; + +class QBBIntegration : public QPlatformIntegration +{ +public: + QBBIntegration(); + virtual ~QBBIntegration(); + + virtual bool hasCapability(QPlatformIntegration::Capability cap) const; + + virtual QPlatformWindow *createPlatformWindow(QWindow *window) const; + virtual QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + + virtual QPlatformInputContext *inputContext() const; + + virtual QList screens() const; + virtual void moveToScreen(QWindow *window, int screen); + + virtual QAbstractEventDispatcher *guiThreadEventDispatcher() const; + + virtual QPlatformFontDatabase *fontDatabase() const { return m_fontDatabase; } + +#ifndef QT_NO_CLIPBOARD + virtual QPlatformClipboard *clipboard() const; +#endif + + virtual QVariant styleHint(StyleHint hint) const; + + bool paintUsingOpenGL() const { return m_paintUsingOpenGL; } + + static QWindow *window(screen_window_t qnxWindow); + +private: + static void addWindow(screen_window_t qnxWindow, QWindow *window); + static void removeWindow(screen_window_t qnxWindow); + + screen_context_t m_screenContext; + QBBEventThread *m_eventThread; + QBBNavigatorThread *m_navigatorThread; + QBBInputContext *m_inputContext; + QPlatformFontDatabase *m_fontDatabase; + bool m_paintUsingOpenGL; + QAbstractEventDispatcher *m_eventDispatcher; +#ifndef QT_NO_CLIPBOARD + mutable QBBClipboard* m_clipboard; +#endif + + static QBBWindowMapper ms_windowMapper; + static QMutex ms_windowMapperMutex; + + friend class QBBWindow; +}; + +QT_END_NAMESPACE + +#endif // QBBINTEGRATION_H diff --git a/src/plugins/platforms/blackberry/qbbkeytranslator.h b/src/plugins/platforms/blackberry/qbbkeytranslator.h new file mode 100644 index 0000000000..fdc1220aba --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbkeytranslator.h @@ -0,0 +1,269 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBKEYTRANSLATOR_H +#define QBBKEYTRANSLATOR_H + +#include + +#if defined(QBBEVENTTHREAD_DEBUG) +#include +#endif + +QT_BEGIN_NAMESPACE + +Qt::Key keyTranslator( int key ) +{ + switch (key) { + case KEYCODE_PAUSE: + return Qt::Key_Pause; + + case KEYCODE_SCROLL_LOCK: + return Qt::Key_ScrollLock; + + case KEYCODE_PRINT: + return Qt::Key_Print; + + case KEYCODE_SYSREQ: + return Qt::Key_SysReq; + +// case KEYCODE_BREAK: + + case KEYCODE_ESCAPE: + return Qt::Key_Escape; + + case KEYCODE_BACKSPACE: + return Qt::Key_Backspace; + + case KEYCODE_TAB: + return Qt::Key_Tab; + + case KEYCODE_BACK_TAB: + return Qt::Key_Backtab; + + case KEYCODE_RETURN: + return Qt::Key_Return; + + case KEYCODE_CAPS_LOCK: + return Qt::Key_CapsLock; + + case KEYCODE_LEFT_SHIFT: + case KEYCODE_RIGHT_SHIFT: + return Qt::Key_Shift; + + case KEYCODE_LEFT_CTRL: + case KEYCODE_RIGHT_CTRL: + return Qt::Key_Control; + + case KEYCODE_LEFT_ALT: + case KEYCODE_RIGHT_ALT: + return Qt::Key_Alt; + + case KEYCODE_MENU: + return Qt::Key_Menu; + + case KEYCODE_LEFT_HYPER: + return Qt::Key_Hyper_L; + + case KEYCODE_RIGHT_HYPER: + return Qt::Key_Hyper_R; + + case KEYCODE_INSERT: + return Qt::Key_Insert; + + case KEYCODE_HOME: + return Qt::Key_Home; + + case KEYCODE_PG_UP: + return Qt::Key_PageUp; + + case KEYCODE_DELETE: + return Qt::Key_Delete; + + case KEYCODE_END: + return Qt::Key_End; + + case KEYCODE_PG_DOWN: + return Qt::Key_PageDown; + + case KEYCODE_LEFT: + return Qt::Key_Left; + + case KEYCODE_RIGHT: + return Qt::Key_Right; + + case KEYCODE_UP: + return Qt::Key_Up; + + case KEYCODE_DOWN: + return Qt::Key_Down; + + case KEYCODE_NUM_LOCK: + return Qt::Key_NumLock; + + case KEYCODE_KP_PLUS: + return Qt::Key_Plus; + + case KEYCODE_KP_MINUS: + return Qt::Key_Minus; + + case KEYCODE_KP_MULTIPLY: + return Qt::Key_Asterisk; + + case KEYCODE_KP_DIVIDE: + return Qt::Key_Slash; + + case KEYCODE_KP_ENTER: + return Qt::Key_Enter; + + case KEYCODE_KP_HOME: + return Qt::Key_Home; + + case KEYCODE_KP_UP: + return Qt::Key_Up; + + case KEYCODE_KP_PG_UP: + return Qt::Key_PageUp; + + case KEYCODE_KP_LEFT: + return Qt::Key_Left; + + // Is this right? + case KEYCODE_KP_FIVE: + return Qt::Key_5; + + case KEYCODE_KP_RIGHT: + return Qt::Key_Right; + + case KEYCODE_KP_END: + return Qt::Key_End; + + case KEYCODE_KP_DOWN: + return Qt::Key_Down; + + case KEYCODE_KP_PG_DOWN: + return Qt::Key_PageDown; + + case KEYCODE_KP_INSERT: + return Qt::Key_Insert; + + case KEYCODE_KP_DELETE: + return Qt::Key_Delete; + + case KEYCODE_F1: + return Qt::Key_F1; + + case KEYCODE_F2: + return Qt::Key_F2; + + case KEYCODE_F3: + return Qt::Key_F3; + + case KEYCODE_F4: + return Qt::Key_F4; + + case KEYCODE_F5: + return Qt::Key_F5; + + case KEYCODE_F6: + return Qt::Key_F6; + + case KEYCODE_F7: + return Qt::Key_F7; + + case KEYCODE_F8: + return Qt::Key_F8; + + case KEYCODE_F9: + return Qt::Key_F9; + + case KEYCODE_F10: + return Qt::Key_F10; + + case KEYCODE_F11: + return Qt::Key_F11; + + case KEYCODE_F12: + return Qt::Key_F12; + + // See keycodes.h for more, but these are all the basics. And printables are already included. + + default: +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: unknown key for translation:" << key; +#endif + break; + } + + return Qt::Key_Escape; +} + +bool isKeypadKey( int key ) +{ + switch (key) + { + case KEYCODE_KP_PLUS: + case KEYCODE_KP_MINUS: + case KEYCODE_KP_MULTIPLY: + case KEYCODE_KP_DIVIDE: + case KEYCODE_KP_ENTER: + case KEYCODE_KP_HOME: + case KEYCODE_KP_UP: + case KEYCODE_KP_PG_UP: + case KEYCODE_KP_LEFT: + case KEYCODE_KP_FIVE: + case KEYCODE_KP_RIGHT: + case KEYCODE_KP_END: + case KEYCODE_KP_DOWN: + case KEYCODE_KP_PG_DOWN: + case KEYCODE_KP_INSERT: + case KEYCODE_KP_DELETE: + return true; + default: + break; + } + + return false; +} + +QT_END_NAMESPACE + +#endif // QBBKEYTRANSLATOR_H diff --git a/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp b/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp new file mode 100644 index 0000000000..65cbb77d31 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbnavigatorthread.h" +#include "qbbscreen.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static const char *navigatorControlPath = "/pps/services/navigator/control"; +static const int ppsBufferSize = 4096; + +QBBNavigatorThread::QBBNavigatorThread(QBBScreen& primaryScreen) + : m_primaryScreen(primaryScreen), + m_fd(-1), + m_readNotifier(0) +{ +} + +QBBNavigatorThread::~QBBNavigatorThread() +{ + // block until thread terminates + shutdown(); + + delete m_readNotifier; +} + +void QBBNavigatorThread::run() +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread started"; +#endif + + // open connection to navigator + errno = 0; + m_fd = open(navigatorControlPath, O_RDWR); + if (m_fd == -1) { + qWarning("QBB: failed to open navigator pps, errno=%d", errno); + return; + } + + m_readNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read); + // using direct connection to get the slot called in this thread's context + connect(m_readNotifier, SIGNAL(activated(int)), this, SLOT(readData()), Qt::DirectConnection); + + exec(); + + // close connection to navigator + close(m_fd); + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread stopped"; +#endif +} + +void QBBNavigatorThread::shutdown() +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread shutdown begin"; +#endif + + // signal thread to terminate + quit(); + + // block until thread terminates + wait(); + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread shutdown end"; +#endif +} + +void QBBNavigatorThread::parsePPS(const QByteArray &ppsData, QByteArray &msg, QByteArray &dat, QByteArray &id) +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: data=" << ppsData; +#endif + + // tokenize pps data into lines + QList lines = ppsData.split('\n'); + + // validate pps object + if (lines.size() == 0 || lines.at(0) != "@control") { + qFatal("QBB: unrecognized pps object, data=%s", ppsData.constData()); + } + + // parse pps object attributes and extract values + for (int i = 1; i < lines.size(); i++) { + + // tokenize current attribute + const QByteArray &attr = lines.at(i); + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: attr=" << attr; +#endif + + int firstColon = attr.indexOf(':'); + if (firstColon == -1) { + // abort - malformed attribute + continue; + } + + int secondColon = attr.indexOf(':', firstColon + 1); + if (secondColon == -1) { + // abort - malformed attribute + continue; + } + + QByteArray key = attr.left(firstColon); + QByteArray value = attr.mid(secondColon + 1); + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: key=" << key; + qDebug() << "PPS: val=" << value; +#endif + + // save attribute value + if (key == "msg") { + msg = value; + } else if (key == "dat") { + dat = value; + } else if (key == "id") { + id = value; + } else { + qFatal("QBB: unrecognized pps attribute, attr=%s", key.constData()); + } + } +} + +void QBBNavigatorThread::replyPPS(const QByteArray &res, const QByteArray &id, const QByteArray &dat) +{ + // construct pps message + QByteArray ppsData = "res::"; + ppsData += res; + ppsData += "\nid::"; + ppsData += id; + if (!dat.isEmpty()) { + ppsData += "\ndat::"; + ppsData += dat; + } + ppsData += "\n"; + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS reply=" << ppsData; +#endif + + // send pps message to navigator + errno = 0; + int bytes = write(m_fd, ppsData.constData(), ppsData.size()); + if (bytes == -1) { + qFatal("QBB: failed to write navigator pps, errno=%d", errno); + } +} + +void QBBNavigatorThread::handleMessage(const QByteArray &msg, const QByteArray &dat, const QByteArray &id) +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: msg=" << msg << ", dat=" << dat << ", id=" << id; +#endif + + // check message type + if (msg == "orientationCheck") { + + // reply to navigator that (any) orientation is acceptable +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: orientation check, o=" << dat; +#endif + replyPPS(msg, id, "true"); + + } else if (msg == "orientation") { + + // update screen geometry and reply to navigator that we're ready +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: orientation, o=" << dat; +#endif + m_primaryScreen.setRotation( dat.toInt() ); + QWindowSystemInterface::handleScreenGeometryChange(0, m_primaryScreen.geometry()); + replyPPS(msg, id, ""); + + } else if (msg == "SWIPE_DOWN") { + + // simulate menu key press +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: menu"; +#endif + QWindow *w = QGuiApplication::focusWindow(); + QWindowSystemInterface::handleKeyEvent(w, QEvent::KeyPress, Qt::Key_Menu, Qt::NoModifier); + QWindowSystemInterface::handleKeyEvent(w, QEvent::KeyRelease, Qt::Key_Menu, Qt::NoModifier); + + } else if (msg == "exit") { + + // shutdown everything +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: exit"; +#endif + QCoreApplication::quit(); + } +} + +void QBBNavigatorThread::readData() +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: reading navigator data"; +#endif + // allocate buffer for pps data + char buffer[ppsBufferSize]; + + // attempt to read pps data + errno = 0; + int bytes = read(m_fd, buffer, ppsBufferSize - 1); + if (bytes == -1) { + qFatal("QBB: failed to read navigator pps, errno=%d", errno); + } + + // check if pps data was received + if (bytes > 0) { + + // ensure data is null terminated + buffer[bytes] = '\0'; + + // process received message + QByteArray ppsData(buffer); + QByteArray msg; + QByteArray dat; + QByteArray id; + parsePPS(ppsData, msg, dat, id); + handleMessage(msg, dat, id); + } +} diff --git a/src/plugins/platforms/blackberry/qbbnavigatorthread.h b/src/plugins/platforms/blackberry/qbbnavigatorthread.h new file mode 100644 index 0000000000..fda24b0c67 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbnavigatorthread.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBNAVIGATORTHREAD_H +#define QBBNAVIGATORTHREAD_H + +#include + +QT_BEGIN_NAMESPACE + +class QBBScreen; +class QSocketNotifier; + +class QBBNavigatorThread : public QThread +{ + Q_OBJECT +public: + QBBNavigatorThread(QBBScreen &primaryScreen); + virtual ~QBBNavigatorThread(); + +protected: + virtual void run(); + +private Q_SLOTS: + void readData(); + +private: + void shutdown(); + void parsePPS(const QByteArray &ppsData, QByteArray &msg, QByteArray &dat, QByteArray &id); + void replyPPS(const QByteArray &res, const QByteArray &id, const QByteArray &dat); + void handleMessage(const QByteArray &msg, const QByteArray &dat, const QByteArray &id); + + QBBScreen &m_primaryScreen; + int m_fd; + QSocketNotifier *m_readNotifier; +}; + +QT_END_NAMESPACE + +#endif // QBBNAVIGATORTHREAD_H diff --git a/src/plugins/platforms/blackberry/qbbrasterbackingstore.cpp b/src/plugins/platforms/blackberry/qbbrasterbackingstore.cpp new file mode 100644 index 0000000000..405e09260f --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrasterbackingstore.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbrasterbackingstore.h" +#include "qbbwindow.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +QBBRasterBackingStore::QBBRasterBackingStore(QWindow *window) + : QPlatformBackingStore(window) +{ +#if defined(QBBRASTERBACKINGSTORE_DEBUG) + qDebug() << "QBBRasterBackingStore::QBBRasterBackingStore - w=" << window; +#endif + + // save platform window associated with widget + m_platformWindow = static_cast(window->handle()); +} + +QBBRasterBackingStore::~QBBRasterBackingStore() +{ +#if defined(QBBRasterBackingStore_DEBUG) + qDebug() << "QBBRasterBackingStore::~QBBRasterBackingStore - w=" << window(); +#endif +} + +QPaintDevice *QBBRasterBackingStore::paintDevice() +{ + return m_platformWindow->renderBuffer().image(); +} + +void QBBRasterBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(window); + Q_UNUSED(offset); + +#if defined(QBBRASTERBACKINGSTORE_DEBUG) + qDebug() << "QBBRasterBackingStore::flush - w=" << this->window(); +#endif + + // visit all pending scroll operations + for (int i = m_scrollOpList.size() - 1; i >= 0; i--) { + + // do the scroll operation + ScrollOp &op = m_scrollOpList[i]; + QRegion srcArea = op.totalArea.intersected( op.totalArea.translated(-op.dx, -op.dy) ); + m_platformWindow->scroll(srcArea, op.dx, op.dy); + } + + // clear all pending scroll operations + m_scrollOpList.clear(); + + // update the display with newly rendered content + m_platformWindow->post(region); +} + +void QBBRasterBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ + Q_UNUSED(size); + Q_UNUSED(staticContents); +#if defined(QBBRASTERBACKINGSTORE_DEBUG) + qDebug() << "QBBRasterBackingStore::resize - w=" << window() << ", s=" << size; +#endif + + // NOTE: defer resizing window buffers until next paint as + // resize() can be called multiple times before a paint occurs +} + +bool QBBRasterBackingStore::scroll(const QRegion &area, int dx, int dy) +{ +#if defined(QBBRASTERBACKINGSTORE_DEBUG) + qDebug() << "QBBRasterBackingStore::scroll - w=" << window(); +#endif + + // calculate entire region affected by scroll operation (src + dst) + QRegion totalArea = area.translated(dx, dy); + totalArea += area; + + // visit all pending scroll operations + for (int i = m_scrollOpList.size() - 1; i >= 0; i--) { + + ScrollOp &op = m_scrollOpList[i]; + if (op.totalArea == totalArea) { + // same area is being scrolled again - update delta + op.dx += dx; + op.dy += dy; + return true; + } else if (op.totalArea.intersects(totalArea)) { + // current scroll overlaps previous scroll but is + // not equal in area - just paint everything + qWarning("QBB: pending scroll operations overlap but not equal"); + return false; + } + } + + // create new scroll operation + m_scrollOpList.append( ScrollOp(totalArea, dx, dy) ); + return true; +} + +void QBBRasterBackingStore::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + +#if defined(QBBRASTERBACKINGSTORE_DEBUG) + qDebug() << "QBBRasterBackingStore::beginPaint - w=" << window(); +#endif + + // resize window buffers if surface resized + QSize s = window()->size(); + if (s != m_platformWindow->bufferSize()) { + m_platformWindow->setBufferSize(s); + } +} + +void QBBRasterBackingStore::endPaint(const QRegion ®ion) +{ + Q_UNUSED(region); +#if defined(QBBRasterBackingStore_DEBUG) + qDebug() << "QBBRasterBackingStore::endPaint - w=" << window(); +#endif +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbrasterbackingstore.h b/src/plugins/platforms/blackberry/qbbrasterbackingstore.h new file mode 100644 index 0000000000..ca1e27bcc8 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrasterbackingstore.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBRASTERWINDOWSURFACE_H +#define QBBRASTERWINDOWSURFACE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBRasterBackingStore : public QPlatformBackingStore +{ +public: + QBBRasterBackingStore(QWindow *window); + virtual ~QBBRasterBackingStore(); + + virtual QPaintDevice *paintDevice(); + virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + virtual void resize(const QSize &size, const QRegion &staticContents); + virtual bool scroll(const QRegion &area, int dx, int dy); + virtual void beginPaint(const QRegion ®ion); + virtual void endPaint(const QRegion ®ion); + +private: + class ScrollOp { + public: + ScrollOp(const QRegion &a, int x, int y) : totalArea(a), dx(x), dy(y) {} + QRegion totalArea; + int dx; + int dy; + }; + + QBBWindow *m_platformWindow; + QList m_scrollOpList; +}; + +QT_END_NAMESPACE + +#endif // QBBRASTERWINDOWSURFACE_H diff --git a/src/plugins/platforms/blackberry/qbbrootwindow.cpp b/src/plugins/platforms/blackberry/qbbrootwindow.cpp new file mode 100644 index 0000000000..831b0774f9 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrootwindow.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbrootwindow.h" + +#include "qbbscreen.h" + +#include + +#if defined(QBBROOTWINDOW_DEBUG) +#include +#endif + +#include +#include + +static const int MAGIC_ZORDER_FOR_NO_NAV = 10; + +QBBRootWindow::QBBRootWindow(QBBScreen *screen) + : m_screen(screen), + m_window(0), + m_windowGroupName() +{ +#if defined(QBBROOTWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Create one top-level QNX window to act as a container for child windows + // since navigator only supports one application window + errno = 0; + int result = screen_create_window(&m_window, m_screen->nativeContext()); + int val[2]; + if (result != 0) { + qFatal("QBBRootWindow: failed to create window, errno=%d", errno); + } + + // Move window to proper display + errno = 0; + screen_display_t display = m_screen->nativeDisplay(); + result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window display, errno=%d", errno); + } + + // Make sure window is above navigator but below keyboard if running as root + // since navigator won't automatically set our z-order in this case + if (getuid() == 0) { + errno = 0; + val[0] = MAGIC_ZORDER_FOR_NO_NAV; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window z-order, errno=%d", errno); + } + } + + // Window won't be visible unless it has some buffers so make one dummy buffer that is 1x1 + errno = 0; + val[0] = SCREEN_USAGE_NATIVE; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window buffer usage, errno=%d", errno); + } + + errno = 0; + val[0] = m_screen->nativeFormat(); + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window pixel format, errno=%d", errno); + } + + errno = 0; + val[0] = 1; + val[1] = 1; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window buffer size, errno=%d", errno); + } + + errno = 0; + result = screen_create_window_buffers(m_window, 1); + if (result != 0) { + qFatal("QBB: failed to create window buffer, errno=%d", errno); + } + + // Window is always the size of the display + errno = 0; + QRect geometry = m_screen->geometry(); + val[0] = geometry.width(); + val[1] = geometry.height(); + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window size, errno=%d", errno); + } + + // Fill the window with solid black + errno = 0; + val[0] = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_COLOR, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window colour, errno=%d", errno); + } + + // Make the window opaque + errno = 0; + val[0] = SCREEN_TRANSPARENCY_NONE; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window transparency, errno=%d", errno); + } + + // Set the swap interval to 1 + errno = 0; + val[0] = 1; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window swap interval, errno=%d", errno); + } + + // Set viewport size equal to window size but move outside buffer so the fill colour is used exclusively + errno = 0; + val[0] = geometry.width(); + val[1] = geometry.height(); + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window source size, errno=%d", errno); + } + + errno = 0; + val[0] = 1; + val[1] = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_POSITION, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window source position, errno=%d", errno); + } + + createWindowGroup(); + post(); +} + +QBBRootWindow::~QBBRootWindow() +{ + // Cleanup top-level QNX window + screen_destroy_window(m_window); +} + +void QBBRootWindow::post() const +{ +#if defined(QBBROOTWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + errno = 0; + screen_buffer_t buffer; + int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&buffer); + if (result != 0) { + qFatal("QBBRootWindow: failed to query window buffer, errno=%d", errno); + } + + errno = 0; + int dirtyRect[] = {0, 0, 1, 1}; + result = screen_post_window(m_window, buffer, 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBB: failed to post window buffer, errno=%d", errno); + } +} + +void QBBRootWindow::flush() const +{ +#if defined(QBBROOTWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Force immediate display update + errno = 0; + int result = screen_flush_context(m_screen->nativeContext(), 0); + if (result != 0) { + qFatal("QBBRootWindow: failed to flush context, errno=%d", errno); + } +} + +void QBBRootWindow::setRotation(int rotation) +{ +#if defined(QBBROOTWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "angle =" << rotation; +#endif + errno = 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window rotation, errno=%d", errno); + } +} + +void QBBRootWindow::resize(const QSize &size) +{ + errno = 0; + int val[] = {size.width(), size.height()}; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window size, errno=%d", errno); + } + + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBRootWindow: failed to set window source size, errno=%d", errno); + } + + // NOTE: display will update when child windows relayout and repaint +} + +void QBBRootWindow::createWindowGroup() +{ + // Generate a random window group name + m_windowGroupName = QUuid::createUuid().toString().toAscii(); + + // Create window group so child windows can be parented by container window + errno = 0; + int result = screen_create_window_group(m_window, m_windowGroupName.constData()); + if (result != 0) { + qFatal("QBBRootWindow: failed to create app window group, errno=%d", errno); + } +} diff --git a/src/plugins/platforms/blackberry/qbbrootwindow.h b/src/plugins/platforms/blackberry/qbbrootwindow.h new file mode 100644 index 0000000000..0b89f52a5e --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrootwindow.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBROOTWINDOW_H +#define QBBROOTWINDOW_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBScreen; + +class QBBRootWindow +{ +public: + QBBRootWindow(QBBScreen *screen); + ~QBBRootWindow(); + + screen_window_t nativeHandle() const { return m_window; } + + void post() const; + void flush() const; + + void setRotation(int rotation); + + void resize(const QSize &size); + + QByteArray groupName() const { return m_windowGroupName; } + +private: + void createWindowGroup(); + + QBBScreen *m_screen; + screen_window_t m_window; + QByteArray m_windowGroupName; +}; + +QT_END_NAMESPACE + +#endif // QBBROOTWINDOW_H diff --git a/src/plugins/platforms/blackberry/qbbscreen.cpp b/src/plugins/platforms/blackberry/qbbscreen.cpp new file mode 100644 index 0000000000..39d2136b7d --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbscreen.cpp @@ -0,0 +1,315 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbscreen.h" +#include "qbbvirtualkeyboard.h" +#include "qbbwindow.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QList QBBScreen::ms_screens; +QList QBBScreen::ms_childWindows; + +QBBScreen::QBBScreen(screen_context_t screenContext, screen_display_t display, bool primaryScreen) + : m_screenContext(screenContext), + m_display(display), + m_rootWindow(), + m_primaryScreen(primaryScreen), + m_posted(false), + m_platformContext(0) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Cache initial orientation of this display + // TODO: use ORIENTATION environment variable? + errno = 0; + int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, &m_initialRotation); + if (result != 0) { + qFatal("QBBScreen: failed to query display rotation, errno=%d", errno); + } + m_currentRotation = m_initialRotation; + + // Cache size of this display in pixels + // TODO: use WIDTH and HEIGHT environment variables? + errno = 0; + int val[2]; + result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to query display size, errno=%d", errno); + } + + m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); + + // Cache size of this display in millimeters + errno = 0; + result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_PHYSICAL_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to query display physical size, errno=%d", errno); + } + + // Peg the DPI to 96 (for now) so fonts are a reasonable size. We'll want to match + // everything with a QStyle later, and at that point the physical size can be used + // instead. + { + static const int dpi = 96; + int width = m_currentGeometry.width() / dpi * qreal(25.4) ; + int height = m_currentGeometry.height() / dpi * qreal(25.4) ; + + m_currentPhysicalSize = m_initialPhysicalSize = QSize(width,height); + } + + // We only create the root window if we are the primary display. + if (primaryScreen) + m_rootWindow = QSharedPointer(new QBBRootWindow(this)); +} + +QBBScreen::~QBBScreen() +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif +} + +/* static */ +void QBBScreen::createDisplays(screen_context_t context) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Query number of displays + errno = 0; + int displayCount; + int result = screen_get_context_property_iv(context, SCREEN_PROPERTY_DISPLAY_COUNT, &displayCount); + if (result != 0) { + qFatal("QBBScreen: failed to query display count, errno=%d", errno); + } + + // Get all displays + errno = 0; + screen_display_t *displays = (screen_display_t *)alloca(sizeof(screen_display_t) * displayCount); + result = screen_get_context_property_pv(context, SCREEN_PROPERTY_DISPLAYS, (void **)displays); + if (result != 0) { + qFatal("QBBScreen: failed to query displays, errno=%d", errno); + } + + for (int i=0; i +#include + +#include + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBScreen : public QPlatformScreen +{ +public: + static QList screens() { return ms_screens; } + static void createDisplays(screen_context_t context); + static void destroyDisplays(); + static QBBScreen *primaryDisplay() { return static_cast(ms_screens.at(0)); } + static int defaultDepth(); + + virtual QRect geometry() const { return m_currentGeometry; } + virtual QRect availableGeometry() const; + virtual int depth() const { return defaultDepth(); } + virtual QImage::Format format() const { return (depth() == 32) ? QImage::Format_RGB32 : QImage::Format_RGB16; } + virtual QSizeF physicalSize() const { return m_currentPhysicalSize; } + + bool isPrimaryScreen() const { return m_primaryScreen; } + + int rotation() const { return m_currentRotation; } + void setRotation(int rotation); + + int nativeFormat() const { return (depth() == 32) ? SCREEN_FORMAT_RGBA8888 : SCREEN_FORMAT_RGB565; } + screen_display_t nativeDisplay() const { return m_display; } + screen_context_t nativeContext() const { return m_screenContext; } + const char *windowGroupName() const { return m_rootWindow->groupName().constData(); } + + /* Window hierarchy management */ + static void addWindow(QBBWindow *child); + static void removeWindow(QBBWindow *child); + static void raiseWindow(QBBWindow *window); + static void lowerWindow(QBBWindow *window); + static void updateHierarchy(); + + void onWindowPost(QBBWindow *window); + + QSharedPointer rootWindow() const { return m_rootWindow; } + +private: + QBBScreen(screen_context_t context, screen_display_t display, bool primaryScreen); + virtual ~QBBScreen(); + + static bool orthogonal(int rotation1, int rotation2); + + screen_context_t m_screenContext; + screen_display_t m_display; + QSharedPointer m_rootWindow; + bool m_primaryScreen; + bool m_posted; + bool m_usingOpenGL; + + int m_initialRotation; + int m_currentRotation; + QSize m_initialPhysicalSize; + QSize m_currentPhysicalSize; + QRect m_initialGeometry; + QRect m_currentGeometry; + QPlatformOpenGLContext *m_platformContext; + + static QList ms_screens; + static QList ms_childWindows; +}; + +QT_END_NAMESPACE + +#endif // QBBSCREEN_H diff --git a/src/plugins/platforms/blackberry/qbbvirtualkeyboard.cpp b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.cpp new file mode 100644 index 0000000000..082ef9b19d --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.cpp @@ -0,0 +1,500 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbvirtualkeyboard.h" +#include "qbbscreen.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *QBBVirtualKeyboard::ms_PPSPath = "/pps/services/input/control?wait"; +const size_t QBBVirtualKeyboard::ms_bufferSize = 2048; + +static QBBVirtualKeyboard *s_instance = 0; + +// Huge hack for keyboard shadow (see QNX PR 88400). Should be removed ASAP. +#define KEYBOARD_SHADOW_HEIGHT 8 + +QBBVirtualKeyboard::QBBVirtualKeyboard() : + m_encoder(NULL), + m_decoder(NULL), + m_buffer(NULL), + m_height(0), + m_fd(-1), + m_keyboardMode(Default), + m_visible(false), + m_locale(QLatin1String("en_US")) +{ + connect(); +} + +QBBVirtualKeyboard::~QBBVirtualKeyboard() +{ + close(); +} + +/* static */ +QBBVirtualKeyboard& QBBVirtualKeyboard::instance() +{ + if (!s_instance) { + s_instance = new QBBVirtualKeyboard(); + s_instance->start(); + } + + return *s_instance; +} + +/* static */ +void QBBVirtualKeyboard::destroy() +{ + if (s_instance) { + delete s_instance; + s_instance = 0; + } +} + +void QBBVirtualKeyboard::close() +{ + if (m_fd) { + // any reads will fail after we close the fd, which is basically what we want. + ::close(m_fd); + } + + // Wait for the thread to die (should be immediate), then continue the cleanup. + wait(); + + if (m_fd) { + m_fd = -1; + } + + + if (m_decoder) + { + pps_decoder_cleanup(m_decoder); + delete m_decoder; + m_decoder = NULL; + } + + if (m_encoder) + { + pps_encoder_cleanup(m_encoder); + delete m_encoder; + m_encoder = NULL; + } + + delete [] m_buffer; + m_buffer = NULL; +} + +bool QBBVirtualKeyboard::connect() +{ + close(); + + m_encoder = new pps_encoder_t; + m_decoder = new pps_decoder_t; + + pps_encoder_initialize(m_encoder, false); + pps_decoder_initialize(m_decoder, NULL); + + errno = 0; + m_fd = ::open(ms_PPSPath, O_RDWR); + if (m_fd == -1) + { + qCritical("QBBVirtualKeyboard: Unable to open \"%s\" for keyboard: %s (%d).", + ms_PPSPath, strerror(errno), errno); + close(); + return false; + } + + m_buffer = new char[ms_bufferSize]; + if (!m_buffer) { + qCritical("QBBVirtualKeyboard: Unable to allocate buffer of %d bytes. Size is unavailable.", ms_bufferSize); + return false; + } + + if (!queryPPSInfo()) + return false; + + start(); + + return true; +} + +bool QBBVirtualKeyboard::queryPPSInfo() +{ + // Request info, requires id to regenerate res message. + pps_encoder_add_string(m_encoder, "msg", "info"); + pps_encoder_add_string(m_encoder, "id", "libWebView"); + + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + return false; + } + + pps_encoder_reset(m_encoder); + + return true; +} + +void QBBVirtualKeyboard::notifyClientActiveStateChange(bool active) +{ + if (!active) + hideKeyboard(); +} + +void QBBVirtualKeyboard::run() +{ + ppsDataReady(); +} + +void QBBVirtualKeyboard::ppsDataReady() +{ + while (1) { + ssize_t nread = read(m_fd, m_buffer, ms_bufferSize - 1); + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: keyboardMessage size: " << nread; +#endif + if (nread < 0) + break; + + // nread is the real space necessary, not the amount read. + if (static_cast(nread) > ms_bufferSize - 1) { + qCritical("QBBVirtualKeyboard: Keyboard buffer size too short; need %u.", nread + 1); + break; + } + + m_buffer[nread] = 0; + pps_decoder_parse_pps_str(m_decoder, m_buffer); + pps_decoder_push(m_decoder, NULL); +#ifdef QBBVIRTUALKEYBOARD_DEBUG + pps_decoder_dump_tree(m_decoder, stderr); +#endif + + const char *value; + if (pps_decoder_get_string(m_decoder, "error", &value) == PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS decoder error: %s", value ? value : "[null]"); + continue; + } + + if (pps_decoder_get_string(m_decoder, "msg", &value) == PPS_DECODER_OK) { + if (strcmp(value, "show") == 0) { + const bool oldVisible = m_visible; + m_visible = true; + handleKeyboardStateChangeMessage(true); + if (oldVisible != m_visible) + emit visibilityChanged(m_visible); + } else if (strcmp(value, "hide") == 0) { + const bool oldVisible = m_visible; + m_visible = false; + handleKeyboardStateChangeMessage(false); + if (oldVisible != m_visible) + emit visibilityChanged(m_visible); + } else if (strcmp(value, "info") == 0) + handleKeyboardInfoMessage(); + else if (strcmp(value, "connect") == 0) { } + else + qCritical("QBBVirtualKeyboard: Unexpected keyboard PPS msg value: %s", value ? value : "[null]"); + } else if (pps_decoder_get_string(m_decoder, "res", &value) == PPS_DECODER_OK) { + if (strcmp(value, "info") == 0) + handleKeyboardInfoMessage(); + else + qCritical("QBBVirtualKeyboard: Unexpected keyboard PPS res value: %s", value ? value : "[null]"); + } else + qCritical("QBBVirtualKeyboard: Unexpected keyboard PPS message type"); + } + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: exiting keyboard thread"; +#endif + + if (m_decoder) + pps_decoder_cleanup(m_decoder); +} + +void QBBVirtualKeyboard::handleKeyboardInfoMessage() +{ + int newHeight = 0; + const char *value; + + if (pps_decoder_push(m_decoder, "dat") != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS dat object not found"); + return; + } + if (pps_decoder_get_int(m_decoder, "size", &newHeight) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS size field not found"); + return; + } + if (pps_decoder_push(m_decoder, "locale") != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS locale object not found"); + return; + } + if (pps_decoder_get_string(m_decoder, "languageId", &value) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS languageId field not found"); + return; + } + const QString languageId = QString::fromLatin1(value); + if (pps_decoder_get_string(m_decoder, "countryId", &value) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS size countryId not found"); + return; + } + const QString countryId = QString::fromLatin1(value); + + // HUGE hack, should be removed ASAP. + newHeight -= KEYBOARD_SHADOW_HEIGHT; // We want to ignore the 8 pixel shadow above the keyboard. (PR 88400) + + if (newHeight != m_height) { + m_height = newHeight; + updateAvailableScreenGeometry(); + } + + const QLocale locale = QLocale(languageId + QLatin1Char('_') + countryId); + if (locale != m_locale) { + m_locale = locale; + emit localeChanged(locale); + } + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: handleKeyboardInfoMessage size=" << m_height << "locale=" << m_locale; +#endif +} + +void QBBVirtualKeyboard::handleKeyboardStateChangeMessage(bool visible) +{ + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: handleKeyboardStateChangeMessage " << visible; +#endif + updateAvailableScreenGeometry(); + + if (visible) + showKeyboard(); + else + hideKeyboard(); +} + +void QBBVirtualKeyboard::updateAvailableScreenGeometry() +{ +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: updateAvailableScreenGeometry: keyboard visible=" << m_visible << ", keyboard height=" << m_height; +#endif + + // TODO: What screen index should be used? I assume primaryScreen here because it works, and + // we do it for handleScreenGeometryChange elsewhere but since we have support + // for more than one screen, that's not going to always work. + QBBScreen *platformScreen = QBBScreen::primaryDisplay(); + QWindowSystemInterface::handleScreenAvailableGeometryChange(platformScreen->screen(), platformScreen->availableGeometry()); +} + +bool QBBVirtualKeyboard::showKeyboard() +{ +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: showKeyboard()"; +#endif + + // Try to connect. + if (m_fd == -1 && !connect()) + return false; + + // NOTE: This must be done everytime the keyboard is shown even if there is no change because + // hiding the keyboard wipes the setting. + applyKeyboardModeOptions(); + + pps_encoder_reset(m_encoder); + + // Send the show message. + pps_encoder_add_string(m_encoder, "msg", "show"); + + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + return false; + } + + pps_encoder_reset(m_encoder); + + // Return true if no error occurs. Sizing response will be triggered when confirmation of + // the change arrives. + return true; +} + +bool QBBVirtualKeyboard::hideKeyboard() +{ +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: hideKeyboard()"; +#endif + + if (m_fd == -1 && !connect()) + return false; + + pps_encoder_add_string(m_encoder, "msg", "hide"); + + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + + //Try again. + if (connect()) { + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + return false; + } + } + else + return false; + } + + pps_encoder_reset(m_encoder); + + // Return true if no error occurs. Sizing response will be triggered when confirmation of + // the change arrives. + return true; +} + +void QBBVirtualKeyboard::setKeyboardMode(KeyboardMode mode) +{ + m_keyboardMode = mode; +} + +void QBBVirtualKeyboard::applyKeyboardModeOptions() +{ + // Try to connect. + if (m_fd == -1 && !connect()) + return; + + // Send the options message. + pps_encoder_add_string(m_encoder, "msg", "options"); + + pps_encoder_start_object(m_encoder, "dat"); + switch (m_keyboardMode) { + case Url: + addUrlModeOptions(); + break; + case Email: + addEmailModeOptions(); + break; + case Web: + addWebModeOptions(); + break; + case NumPunc: + addNumPuncModeOptions(); + break; + case Symbol: + addSymbolModeOptions(); + break; + case Phone: + addPhoneModeOptions(); + break; + case Pin: + addPinModeOptions(); + break; + case Default: + default: + addDefaultModeOptions(); + break; + } + + pps_encoder_end_object(m_encoder); + + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + } + + pps_encoder_reset(m_encoder); +} + +void QBBVirtualKeyboard::addDefaultModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "default"); +} + +void QBBVirtualKeyboard::addUrlModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "url"); +} + +void QBBVirtualKeyboard::addEmailModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "email"); +} + +void QBBVirtualKeyboard::addWebModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "web"); +} + +void QBBVirtualKeyboard::addNumPuncModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "numPunc"); +} + +void QBBVirtualKeyboard::addPhoneModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "phone"); +} + +void QBBVirtualKeyboard::addPinModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "pin"); +} + +void QBBVirtualKeyboard::addSymbolModeOptions() +{ + pps_encoder_add_string(m_encoder, "enter", "enter.default"); + pps_encoder_add_string(m_encoder, "type", "symbol"); +} diff --git a/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h new file mode 100644 index 0000000000..14003ee897 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h @@ -0,0 +1,130 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIRTUALKEYBOARD_H_ +#define VIRTUALKEYBOARD_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* Shamelessly copied from the browser - this should be rewritten once we have a proper PPS wrapper class */ +class QBBVirtualKeyboard : public QThread +{ + Q_OBJECT +public: + // NOTE: Not all the following keyboard modes are currently used. + // Default - Regular Keyboard + // Url/Email - Enhanced keys for each types. + // Web - Regular keyboard with two blank keys, currently unused. + // NumPunc - Numbers & Punctionation, alternate to Symbol + // Symbol - All symbols, alternate to NumPunc, currently unused. + // Phone - Phone enhanced keyboard - currently unused as no alternate keyboard available to access a-zA-Z + // Pin - Keyboard for entering Pins (Hex values) currently unused. + // + // SPECIAL NOTE: Usage of NumPunc may have to be removed, ABC button is non-functional. + // + enum KeyboardMode { Default, Url, Email, Web, NumPunc, Symbol, Phone, Pin }; + + static QBBVirtualKeyboard& instance(); + static void destroy(); + + bool showKeyboard(); + bool hideKeyboard(); + int height() { return m_visible ? m_height : 0; } + void setKeyboardMode(KeyboardMode); + void notifyClientActiveStateChange(bool); + bool isVisible() const { return m_visible; } + QLocale locale() const { return m_locale; } + +Q_SIGNALS: + void localeChanged(const QLocale &locale); + void visibilityChanged(bool visible); + +private: + QBBVirtualKeyboard(); + virtual ~QBBVirtualKeyboard(); + + // Will be called internally if needed. + bool connect(); + void close(); + void ppsDataReady(); + bool queryPPSInfo(); + void handleKeyboardInfoMessage(); + void handleKeyboardStateChangeMessage(bool visible); + void updateAvailableScreenGeometry(); + + void applyKeyboardModeOptions(); + void addDefaultModeOptions(); + void addUrlModeOptions(); + void addEmailModeOptions(); + void addWebModeOptions(); + void addNumPuncModeOptions(); + void addSymbolModeOptions(); + void addPhoneModeOptions(); + void addPinModeOptions(); + + // QThread overrides + virtual void run(); + + pps_encoder_t *m_encoder; + pps_decoder_t *m_decoder; + char *m_buffer; + int m_height; + int m_fd; + KeyboardMode m_keyboardMode; + bool m_visible; + QLocale m_locale; + + // Path to keyboardManager in PPS. + static const char *ms_PPSPath; + static const size_t ms_bufferSize; +}; + +#endif /* VIRTUALKEYBOARD_H_ */ diff --git a/src/plugins/platforms/blackberry/qbbwindow.cpp b/src/plugins/platforms/blackberry/qbbwindow.cpp new file mode 100644 index 0000000000..bc9f112b01 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbwindow.cpp @@ -0,0 +1,665 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbwindow.h" +#include "qbbglcontext.h" +#include "qbbintegration.h" +#include "qbbscreen.h" + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +QBBWindow::QBBWindow(QWindow *window, screen_context_t context) + : QPlatformWindow(window), + m_screenContext(context), + m_window(0), + m_currentBufferIndex(-1), + m_previousBufferIndex(-1), + m_platformOpenGLContext(0), + m_screen(0), + m_parentWindow(0), + m_visible(true) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size(); +#endif + int result; + + // Create child QNX window + errno = 0; + result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + if (result != 0) { + qFatal("QBBWindow: failed to create window, errno=%d", errno); + } + + // Set window buffer usage based on rendering API + int val; + QSurface::SurfaceType surfaceType = window->surfaceType(); + switch (surfaceType) { + case QSurface::RasterSurface: + val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE; + break; + case QSurface::OpenGLSurface: + val = SCREEN_USAGE_OPENGL_ES2; + break; + default: + qFatal("QBBWindow: unsupported window API"); + break; + } + + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window buffer usage, errno=%d", errno); + } + + // Alpha channel is always pre-multiplied if present + errno = 0; + val = SCREEN_PRE_MULTIPLIED_ALPHA; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window alpha mode, errno=%d", errno); + } + + // Make the window opaque + errno = 0; + val = SCREEN_TRANSPARENCY_NONE; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window transparency, errno=%d", errno); + } + + // Set the window swap interval + errno = 0; + val = 1; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window swap interval, errno=%d", errno); + } + + // Assign the window to the primary display (this is the default specified by screen). + setScreen(QBBScreen::primaryDisplay()); + + // Add the window to the root of the hierarchy + QBBScreen::addWindow(this); + + // Add window to plugin's window mapper + QBBIntegration::addWindow(m_window, window); +} + +QBBWindow::~QBBWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Remove from plugin's window mapper + QBBIntegration::removeWindow(m_window); + + // Remove from parent's Hierarchy. + removeFromParent(); + QBBScreen::updateHierarchy(); + + // We shouldn't allow this case unless QT allows it. Does it? Or should we send the + // handleCloseEvent on all children when this window is deleted? + if (m_childWindows.size() > 0) + qFatal("QBBWindow: window destroyed before children!"); + + // Cleanup QNX window and its buffers + screen_destroy_window(m_window); +} + +void QBBWindow::setGeometry(const QRect &rect) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << ", (" << rect.x() << "," << rect.y() << "," << rect.width() << "," << rect.height() << ")"; +#endif + + QRect oldGeometry = geometry(); + + // Call base class method + QPlatformWindow::setGeometry(rect); + + // Set window geometry equal to widget geometry + errno = 0; + int val[2]; + val[0] = rect.x(); + val[1] = rect.y(); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window position, errno=%d", errno); + } + + errno = 0; + val[0] = rect.width(); + val[1] = rect.height(); + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window size, errno=%d", errno); + } + + // Set viewport size equal to window size + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window source size, errno=%d", errno); + } + + // Now move all children. + QPoint offset; + if (!oldGeometry.isEmpty()) { + offset = rect.topLeft(); + offset -= oldGeometry.topLeft(); + + QList::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->offset(offset); + } + } +} + +void QBBWindow::offset(const QPoint &offset) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Move self and then children. + QRect newGeometry = geometry(); + newGeometry.translate(offset); + + // Call the base class + QPlatformWindow::setGeometry(newGeometry); + + int val[2]; + + errno = 0; + val[0] = newGeometry.x(); + val[1] = newGeometry.y(); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window position, errno=%d", errno); + } + + QList::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->offset(offset); + } +} + +void QBBWindow::setVisible(bool visible) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "visible =" << visible; +#endif + + m_visible = visible; + + QBBWindow *root = this; + while (root->m_parentWindow) + root = root->m_parentWindow; + + root->updateVisibility(root->m_visible); + + window()->requestActivateWindow(); +} + +void QBBWindow::updateVisibility(bool parentVisible) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "parentVisible =" << parentVisible << "window =" << window(); +#endif + // Set window visibility + errno = 0; + int val = (m_visible && parentVisible) ? 1 : 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window visibility, errno=%d", errno); + } + + QList::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->updateVisibility(m_visible && parentVisible); + } +} + +void QBBWindow::setOpacity(qreal level) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "opacity =" << level; +#endif + // Set window global alpha + errno = 0; + int val = (int)(level * 255); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window global alpha, errno=%d", errno); + } + + // TODO: How to handle children of this window? If we change all the visibilities, then + // the transparency will look wrong... +} + +void QBBWindow::setBufferSize(const QSize &size) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "size =" << size; +#endif + // Set window buffer size + errno = 0; + int val[2] = { size.width(), size.height() }; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window buffer size, errno=%d", errno); + } + + // Create window buffers if they do not exist + if (!hasBuffers()) { + // Get pixel format from EGL config if using OpenGL; + // otherwise inherit pixel format of window's screen + if (m_platformOpenGLContext != 0) { + val[0] = platformWindowFormatToNativeFormat(m_platformOpenGLContext->format()); + } else { + val[0] = m_screen->nativeFormat(); + } + + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window pixel format, errno=%d", errno); + } + + errno = 0; + result = screen_create_window_buffers(m_window, MAX_BUFFER_COUNT); + if (result != 0) { + qFatal("QBBWindow: failed to create window buffers, errno=%d", errno); + } + } + + // Cache new buffer size + m_bufferSize = size; + + // Buffers were destroyed; reacquire them + m_currentBufferIndex = -1; + m_previousDirty = QRegion(); + m_scrolled = QRegion(); +} + +QBBBuffer &QBBWindow::renderBuffer() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Check if render buffer is invalid + if (m_currentBufferIndex == -1) { + // Get all buffers available for rendering + errno = 0; + screen_buffer_t buffers[MAX_BUFFER_COUNT]; + int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)buffers); + if (result != 0) { + qFatal("QBBWindow: failed to query window buffers, errno=%d", errno); + } + + // Wrap each buffer + for (int i = 0; i < MAX_BUFFER_COUNT; ++i) { + m_buffers[i] = QBBBuffer(buffers[i]); + } + + // Use the first available render buffer + m_currentBufferIndex = 0; + m_previousBufferIndex = -1; + } + + return m_buffers[m_currentBufferIndex]; +} + +void QBBWindow::scroll(const QRegion ®ion, int dx, int dy, bool flush) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + copyBack(region, dx, dy, flush); + m_scrolled += region; +} + +void QBBWindow::post(const QRegion &dirty) +{ + // Check if render buffer exists and something was rendered + if (m_currentBufferIndex != -1 && !dirty.isEmpty()) { +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::post - window =" << window(); +#endif + QBBBuffer ¤tBuffer = m_buffers[m_currentBufferIndex]; + + // Copy unmodified region from old render buffer to new render buffer; + // required to allow partial updates + QRegion preserve = m_previousDirty - dirty - m_scrolled; + copyBack(preserve, 0, 0); + + // Calculate region that changed + QRegion modified = preserve + dirty + m_scrolled; + QRect rect = modified.boundingRect(); + int dirtyRect[4] = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() }; + + // Update the display with contents of render buffer + errno = 0; + int result = screen_post_window(m_window, currentBuffer.nativeBuffer(), 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBBWindow: failed to post window buffer, errno=%d", errno); + } + + // Advance to next nender buffer + m_previousBufferIndex = m_currentBufferIndex++; + if (m_currentBufferIndex >= MAX_BUFFER_COUNT) { + m_currentBufferIndex = 0; + } + + // Save modified region and clear scrolled region + m_previousDirty = dirty; + m_scrolled = QRegion(); + + // Notify screen that window posted + if (m_screen != 0) { + m_screen->onWindowPost(this); + } + } +} + +void QBBWindow::setScreen(QBBScreen *platformScreen) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "platformScreen =" << platformScreen; +#endif + + if (m_screen == platformScreen) + return; + + m_screen = platformScreen; + + // Move window to proper screen/display + errno = 0; + screen_display_t display = platformScreen->nativeDisplay(); + int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); + if (result != 0) { + qFatal("QBBWindow: 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("QBBWindow: failed to join window group, errno=%d", errno); + } + + QList::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + // Only subwindows and tooltips need necessarily be moved to another display with the window. + if ((window()->windowType() & Qt::WindowType_Mask) == Qt::SubWindow || + (window()->windowType() & Qt::WindowType_Mask) == Qt::ToolTip) + (*it)->setScreen(platformScreen); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::removeFromParent() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Remove from old Hierarchy position + if (m_parentWindow) { + if (m_parentWindow->m_childWindows.removeAll(this)) + m_parentWindow = 0; + else + qFatal("QBBWindow: Window Hierarchy broken; window has parent, but parent hasn't got child."); + } else { + QBBScreen::removeWindow(this); + } +} + +void QBBWindow::setParent(const QPlatformWindow *window) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << this->window() << "platformWindow =" << window; +#endif + // Cast away the const, we need to modify the hierarchy. + QBBWindow *newParent = 0; + + if (window) + newParent = static_cast((QPlatformWindow *)window); + + if (newParent == m_parentWindow) + return; + + removeFromParent(); + m_parentWindow = newParent; + + // Add to new hierarchy position. + if (m_parentWindow) { + if (m_parentWindow->m_screen != m_screen) + setScreen(m_parentWindow->m_screen); + + m_parentWindow->m_childWindows.push_back(this); + } else { + QBBScreen::addWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::raise() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + QBBWindow *oldParent = m_parentWindow; + if (oldParent) { + removeFromParent(); + oldParent->m_childWindows.push_back(this); + } else { + QBBScreen::raiseWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::lower() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + QBBWindow *oldParent = m_parentWindow; + if (oldParent) { + removeFromParent(); + oldParent->m_childWindows.push_front(this); + } else { + QBBScreen::lowerWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::requestActivateWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + // TODO: Tell screen to set keyboard focus to this window. + + // Notify that we gained focus. + gainedFocus(); +} + +void QBBWindow::gainedFocus() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + // Got focus + QWindowSystemInterface::handleWindowActivated(window()); +} + +void QBBWindow::setPlatformOpenGLContext(QBBGLContext *platformOpenGLContext) +{ + // This function does not take ownership of the platform gl context. + // It is owned by the frontend QOpenGLContext + m_platformOpenGLContext = platformOpenGLContext; +} + +void QBBWindow::updateZorder(int &topZorder) +{ + errno = 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, &topZorder); + topZorder++; + + if (result != 0) + qFatal("QBBWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, m_window); + + QList::const_iterator it; + + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) + (*it)->updateZorder(topZorder); +} + +void QBBWindow::copyBack(const QRegion ®ion, int dx, int dy, bool flush) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + int result; + + // Abort if previous buffer is invalid + if (m_previousBufferIndex == -1) { + return; + } + + // Abort if nothing to copy + if (region.isEmpty()) { + return; + } + + QBBBuffer ¤tBuffer = m_buffers[m_currentBufferIndex]; + QBBBuffer &previousBuffer = m_buffers[m_previousBufferIndex]; + + // Break down region into non-overlapping rectangles + QVector rects = region.rects(); + for (int i = rects.size() - 1; i >= 0; i--) { + // Clip rectangle to bounds of target + QRect rect = rects[i].intersected( currentBuffer.rect() ); + + if (rect.isEmpty()) + continue; + + // Setup blit operation + int attribs[] = { SCREEN_BLIT_SOURCE_X, rect.x(), + SCREEN_BLIT_SOURCE_Y, rect.y(), + SCREEN_BLIT_SOURCE_WIDTH, rect.width(), + SCREEN_BLIT_SOURCE_HEIGHT, rect.height(), + SCREEN_BLIT_DESTINATION_X, rect.x() + dx, + SCREEN_BLIT_DESTINATION_Y, rect.y() + dy, + SCREEN_BLIT_DESTINATION_WIDTH, rect.width(), + SCREEN_BLIT_DESTINATION_HEIGHT, rect.height(), + SCREEN_BLIT_END }; + + // Queue blit operation + errno = 0; + result = screen_blit(m_screenContext, currentBuffer.nativeBuffer(), previousBuffer.nativeBuffer(), attribs); + if (result != 0) { + qFatal("QBBWindow: failed to blit buffers, errno=%d", errno); + } + } + + // Check if flush requested + if (flush) { + // Wait for all blits to complete + errno = 0; + result = screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE); + if (result != 0) { + qFatal("QBBWindow: failed to flush blits, errno=%d", errno); + } + + // Buffer was modified outside the CPU + currentBuffer.invalidateInCache(); + } +} + +int QBBWindow::platformWindowFormatToNativeFormat(const QSurfaceFormat &format) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Extract size of colour channels from window format + int redSize = format.redBufferSize(); + if (redSize == -1) { + qFatal("QBBWindow: red size not defined"); + } + + int greenSize = format.greenBufferSize(); + if (greenSize == -1) { + qFatal("QBBWindow: green size not defined"); + } + + int blueSize = format.blueBufferSize(); + if (blueSize == -1) { + qFatal("QBBWindow: blue size not defined"); + } + + // select matching native format + if (redSize == 5 && greenSize == 6 && blueSize == 5) { + return SCREEN_FORMAT_RGB565; + } else if (redSize == 8 && greenSize == 8 && blueSize == 8) { + return SCREEN_FORMAT_RGBA8888; + } else { + qFatal("QBBWindow: unsupported pixel format"); + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbwindow.h b/src/plugins/platforms/blackberry/qbbwindow.h new file mode 100644 index 0000000000..ffdeba423a --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbwindow.h @@ -0,0 +1,133 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBWINDOW_H +#define QBBWINDOW_H + +#include + +#include "qbbbuffer.h" + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +// all surfaces double buffered +#define MAX_BUFFER_COUNT 2 + +class QBBGLContext; +class QBBScreen; + +class QPlatformGLContext; +class QSurfaceFormat; + +class QBBWindow : public QPlatformWindow +{ +friend class QBBScreen; +public: + QBBWindow(QWindow *window, screen_context_t context); + virtual ~QBBWindow(); + + virtual void setGeometry(const QRect &rect); + virtual void setVisible(bool visible); + virtual void setOpacity(qreal level); + + virtual WId winId() const { return (WId)m_window; } + screen_window_t nativeHandle() const { return m_window; } + + void setBufferSize(const QSize &size); + QSize bufferSize() const { return m_bufferSize; } + bool hasBuffers() const { return !m_bufferSize.isEmpty(); } + + QBBBuffer &renderBuffer(); + void scroll(const QRegion ®ion, int dx, int dy, bool flush=false); + void post(const QRegion &dirty); + + void setScreen(QBBScreen *platformScreen); + + virtual void setParent(const QPlatformWindow *window); + virtual void raise(); + virtual void lower(); + virtual void requestActivateWindow(); + + void gainedFocus(); + + QBBScreen *screen() const { return m_screen; } + const QList& children() const { return m_childWindows; } + + void setPlatformOpenGLContext(QBBGLContext *platformOpenGLContext); + QBBGLContext *platformOpenGLContext() const { return m_platformOpenGLContext; } + +private: + void removeFromParent(); + void offset(const QPoint &offset); + void updateVisibility(bool parentVisible); + void updateZorder(int &topZorder); + + void fetchBuffers(); + + void copyBack(const QRegion ®ion, int dx, int dy, bool flush=false); + + static int platformWindowFormatToNativeFormat(const QSurfaceFormat &format); + + screen_context_t m_screenContext; + screen_window_t m_window; + QSize m_bufferSize; + QBBBuffer m_buffers[MAX_BUFFER_COUNT]; + int m_currentBufferIndex; + int m_previousBufferIndex; + QRegion m_previousDirty; + QRegion m_scrolled; + + QBBGLContext *m_platformOpenGLContext; + QBBScreen *m_screen; + QList m_childWindows; + QBBWindow *m_parentWindow; + bool m_visible; +}; + +QT_END_NAMESPACE + +#endif // QBBWINDOW_H diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index c2e6d8cea7..8f728a5fb0 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -11,3 +11,7 @@ mac { } win32: SUBDIRS += windows + +blackberry-armv7le-qcc { + SUBDIRS += blackberry +} -- cgit v1.2.3