diff options
author | Rafael Roquetto <rafael.roquetto@kdab.com> | 2012-02-07 19:01:40 (GMT) |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-02-21 06:37:02 (GMT) |
commit | c87445a2b75264e633d2f358534109ee8ca27ca9 (patch) | |
tree | 207e679057de4868b38021f41a11fc7e77aac3f6 | |
parent | ae39f2286b6fb5ddc63223bab4b3fab3736b4ee8 (diff) | |
download | Qt-c87445a2b75264e633d2f358534109ee8ca27ca9.zip Qt-c87445a2b75264e633d2f358534109ee8ca27ca9.tar.gz Qt-c87445a2b75264e633d2f358534109ee8ca27ca9.tar.bz2 |
Initial import of the BlackBerry Playbook QPA plugin.
Change-Id: Ibc883be9af145eafef2e5ff9eb82c3364bace331
Reviewed-by: Sean Harmer <sh@theharmers.co.uk>
Reviewed-by: Jørgen Lind <jorgen.lind@nokia.com>
33 files changed, 7381 insertions, 0 deletions
diff --git a/src/plugins/platforms/blackberry/blackberry.pro b/src/plugins/platforms/blackberry/blackberry.pro new file mode 100644 index 0000000..0d3cf43 --- /dev/null +++ b/src/plugins/platforms/blackberry/blackberry.pro @@ -0,0 +1,44 @@ +TARGET = blackberry +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms +QT += opengl + +SOURCES = main.cpp \ + qbbbuffer.cpp \ + qbbeventthread.cpp \ + qbbglcontext.cpp \ + qbbglwindowsurface.cpp \ + qbbinputcontext.cpp \ + qbbintegration.cpp \ + qbbnavigatorthread.cpp \ + qbbscreen.cpp \ + qbbwindow.cpp \ + qbbrasterwindowsurface.cpp \ + qbbvirtualkeyboard.cpp \ + qbbclipboard.cpp \ + qbblocalethread.cpp + +HEADERS = qbbbuffer.h \ + qbbeventthread.h \ + qbbinputcontext.h \ + qbbintegration.h \ + qbbnavigatorthread.h \ + qbbglcontext.h \ + qbbglwindowsurface.h \ + qbbscreen.h \ + qbbwindow.h \ + qbbrasterwindowsurface.h \ + qbbvirtualkeyboard.h \ + qbbclipboard.h \ + qbblocalethread.h + +QMAKE_CXXFLAGS += -I./private + +LIBS += -lpps -lscreen -lEGL -lclipboard + +include (../eglconvenience/eglconvenience.pri) +include (../fontdatabases/genericunix/genericunix.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 0000000..ff0baaa --- /dev/null +++ b/src/plugins/platforms/blackberry/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtGui/QPlatformIntegrationPlugin> +#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 << "blackberry"; + return list; +} + +QPlatformIntegration *QBBIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == "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 0000000..00476da --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbbuffer.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBBUFFER_DEBUG + + +#include "qbbbuffer.h" + +#include <QDebug> + +#include <errno.h> +#include <sys/mman.h> + +QT_BEGIN_NAMESPACE + +QBBBuffer::QBBBuffer() + : mBuffer(NULL) +{ +#if defined(QBBBUFFER_DEBUG) + qDebug() << "QBBBuffer::QBBBuffer - empty"; +#endif +} + +QBBBuffer::QBBBuffer(screen_buffer_t buffer) + : mBuffer(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 = NULL; + 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 + mImage = QImage(dataPtr, size[0], size[1], stride, imageFormat); +} + +QBBBuffer::QBBBuffer(const QBBBuffer &other) + : mBuffer(other.mBuffer), + mImage(other.mImage) +{ +#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 (mBuffer == NULL) { + qFatal("QBB: can't invalidate cache for null buffer"); + } + + // evict buffer's data from cache + errno = 0; + int result = msync(mImage.bits(), mImage.height() * mImage.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 0000000..70c7ee8 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbbuffer.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBBUFFER_H +#define QBBBUFFER_H + +#include <QImage> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +class QBBBuffer +{ +public: + QBBBuffer(); + QBBBuffer(screen_buffer_t buffer); + QBBBuffer(const QBBBuffer &other); + virtual ~QBBBuffer(); + + screen_buffer_t nativeBuffer() const { return mBuffer; } + const QImage *image() const { return (mBuffer != NULL) ? &mImage : NULL; } + QImage *image() { return (mBuffer != NULL) ? &mImage : NULL; } + + QRect rect() const { return mImage.rect(); } + + void invalidateInCache(); + +private: + screen_buffer_t mBuffer; + QImage mImage; +}; + +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 0000000..3f7d2d5 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbclipboard.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBCLIPBOARD_DEBUG + +#ifndef QT_NO_CLIPBOARD + +#include "qbbclipboard.h" +#include <QUrl> +#include <QStringList> +#include <QColor> +#include <QDebug> + +#include <clipboard/clipboard.h> +#include <errno.h> + +QT_BEGIN_NAMESPACE +static const char *typeList[] = {"text/html", "text/plain", "application/x-color"}; + +QBBClipboard::QBBClipboard() +{ + mMimeData = 0; +} + +QBBClipboard::~QBBClipboard() +{ + delete mMimeData; +} + +void QBBClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return; + + if (mMimeData != data) { + delete mMimeData; + mMimeData = 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; +#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 + mMimeData->setData(qtype, QByteArray(pbuffer, size)); + delete pbuffer; + } + } +} + +QMimeData* QBBClipboard::mimeData(QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return 0; + + if (!mMimeData) + mMimeData = new QMimeData(); + + mMimeData->clear(); + + for (int i = 0; i < 3; i++) + readClipboardBuff(typeList[i]); + + return mMimeData; +} + +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 0000000..1241bf0 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbclipboard.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBCLIPBOARD_H +#define QBBCLIPBOARD_H + +#ifndef QT_NO_CLIPBOARD +#include <QtGui/QPlatformClipboard> +#include <QMimeData> + +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 *mMimeData; + void readClipboardBuff(const char *type); +}; + +QT_END_NAMESPACE + +#endif //QT_NO_CLIPBOAR +#endif //QBBCLIPBOARD_H diff --git a/src/plugins/platforms/blackberry/qbbeventthread.cpp b/src/plugins/platforms/blackberry/qbbeventthread.cpp new file mode 100644 index 0000000..29dc9a7 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbeventthread.cpp @@ -0,0 +1,613 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBEVENTTHREAD_DEBUG + +#include "qbbeventthread.h" +#include "qbbinputcontext.h" +#include "qbbkeytranslator.h" + +#include <QtGui/QWidget> +#include <QtGui/QPlatformScreen> +#include <QtGui/QApplication> +#include <QDebug> + +#include <errno.h> +#include <unistd.h> +#include <sys/keycodes.h> + +#include <cctype> + +QBBEventThread::QBBEventThread(screen_context_t context, QPlatformScreen& screen) + : mContext(context), + mScreen(screen), + mQuit(false), + mLastButtonState(Qt::NoButton), + mLastMouseWindow(0) +{ + // initialize array of touch points + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + + // map array index to id + mTouchPoints[i].id = i; + + // first finger is primary + mTouchPoints[i].isPrimary = (i == 0); + + // pressure is not supported - use default + mTouchPoints[i].pressure = 1.0; + + // nothing touching + mTouchPoints[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 (!mQuit) { + + // block until screen event is available + errno = 0; + result = screen_get_event(mContext, 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(mContext, 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 + mQuit = 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); + } + + // Let the input context have a stab at it first. + QWidget *focusWidget = QApplication::focusWidget(); + if (focusWidget) { + QInputContext* inputContext = focusWidget->inputContext(); + if (inputContext) { + QBBInputContext *qbbInputContext = dynamic_cast<QBBInputContext *>(inputContext); + + if (qbbInputContext && qbbInputContext->handleKeyboardEvent(flags, sym, modifiers, scan, cap)) + return; + } + } + + 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(QApplication::activeWindow(), type, key, qtMod, keyStr); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt key t=" << type << ", k=" << key << ", s=" << keyStr; +#endif + } +} + +void QBBEventThread::injectPointerMoveEvent(int x, int y) +{ +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "Injecting mouse event..." << x << y; +#endif + + QWidget *w = qApp->topLevelAt(x, y); + void *qnxWindow = w ? w->platformWindow() : 0; + + // Generate enter and leave events as needed. + if (qnxWindow != mLastMouseWindow) { + QWidget* wOld = QWidget::find( (WId)mLastMouseWindow ); + + 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 + } + } + + mLastMouseWindow = qnxWindow; + + // convert point to local coordinates + QPoint globalPoint(x, y); + QPoint localPoint(x, y); + + if (w) + localPoint = QPoint(x - w->x(), y - w->y()); + + // Convert buttons. + Qt::MouseButtons buttons = mLastButtonState; + + if (w) { + // Inject mouse event into Qt only if something has changed. + if (mLastGlobalMousePoint != globalPoint || + mLastLocalMousePoint != localPoint || + mLastButtonState != buttons) { + QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << (int)buttons; +#endif + } + } + + mLastGlobalMousePoint = globalPoint; + mLastLocalMousePoint = localPoint; + mLastButtonState = buttons; +} + +void QBBEventThread::handlePointerEvent(screen_event_t event) +{ + errno = 0; + + // Query the window that was clicked + void *qnxWindow; + int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &qnxWindow); + if (result) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + + // 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 to top-level widget + QWidget* w = QWidget::find( (WId)qnxWindow ); + + // Generate enter and leave events as needed. + if (qnxWindow != mLastMouseWindow) { + QWidget* wOld = QWidget::find( (WId)mLastMouseWindow ); + + 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 + } + } + mLastMouseWindow = 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 (mLastGlobalMousePoint != globalPoint || + mLastLocalMousePoint != localPoint || + mLastButtonState != buttons) { + QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons); +#if defined(QBBEVENTTHREAD_DEBUG) + qDebug() << "QBB: Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << (int)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=" << (int)wheelDelta; +#endif + } + } + + mLastGlobalMousePoint = globalPoint; + mLastLocalMousePoint = localPoint; + mLastButtonState = 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 *qnxWindow; + result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &qnxWindow); + if (result) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + + // check if finger is valid + if (touchId < MAX_TOUCH_POINTS) { + + // map window to top-level widget + QWidget* w = QWidget::find( (WId)qnxWindow ); + + // Generate enter and leave events as needed. + if (qnxWindow != mLastMouseWindow) { + QWidget* wOld = QWidget::find( (WId)mLastMouseWindow ); + + 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 + } + } + mLastMouseWindow = 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::platformScreenForWidget(w); + QSize screenSize = platformScreen->physicalSize(); + + // update cached position of current touch point + mTouchPoints[touchId].normalPosition = QPointF( ((qreal)pos[0]) / screenSize.width(), ((qreal)pos[1]) / screenSize.height() ); + mTouchPoints[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: + mTouchPoints[touchId].state = Qt::TouchPointPressed; + type = QEvent::TouchBegin; + break; + case SCREEN_EVENT_MTOUCH_MOVE: + mTouchPoints[touchId].state = Qt::TouchPointMoved; + type = QEvent::TouchUpdate; + break; + case SCREEN_EVENT_MTOUCH_RELEASE: + mTouchPoints[touchId].state = Qt::TouchPointReleased; + type = QEvent::TouchEnd; + break; + } + + // build list of active touch points + QList<QWindowSystemInterface::TouchPoint> pointList; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (i == touchId) { + // current touch point is always active + pointList.append(mTouchPoints[i]); + } else if (mTouchPoints[i].state != Qt::TouchPointReleased) { + // finger is down but did not move + mTouchPoints[i].state = Qt::TouchPointStationary; + pointList.append(mTouchPoints[i]); + } + } + + // inject event into Qt + QWindowSystemInterface::handleTouchEvent(w, type, QTouchEvent::TouchScreen, 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 *qnxWindow; + int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &qnxWindow); + if (result != 0) { + qFatal("QBB: failed to query event window, errno=%d", errno); + } + + // map window to top-level widget + QWidget* w = QWidget::find( (WId)qnxWindow ); + if (w != NULL) { + QWindowSystemInterface::handleCloseEvent(w); + } +} + diff --git a/src/plugins/platforms/blackberry/qbbeventthread.h b/src/plugins/platforms/blackberry/qbbeventthread.h new file mode 100644 index 0000000..a4557fe --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbeventthread.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBEVENTTHREAD_H +#define QBBEVENTTHREAD_H + +#include <QtGui/QPlatformScreen> +#include <QtGui/QWindowSystemInterface> +#include <QThread> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +// an arbitrary number +#define MAX_TOUCH_POINTS 10 + +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); + void injectPointerMoveEvent(int x, int y); + +protected: + virtual void run(); + +private: + screen_context_t mContext; + QPlatformScreen& mScreen; + bool mQuit; + QPoint mLastGlobalMousePoint; + QPoint mLastLocalMousePoint; + Qt::MouseButtons mLastButtonState; + void* mLastMouseWindow; + + QWindowSystemInterface::TouchPoint mTouchPoints[MAX_TOUCH_POINTS]; + + 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); +}; + +QT_END_NAMESPACE + +#endif // QBBEVENTTHREAD_H diff --git a/src/plugins/platforms/blackberry/qbbglcontext.cpp b/src/plugins/platforms/blackberry/qbbglcontext.cpp new file mode 100644 index 0000000..ff61970 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglcontext.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBGLCONTEXT_DEBUG + + +#include "qbbglcontext.h" +#include "qbbscreen.h" +#include "qbbwindow.h" +#include "../eglconvenience/qeglconvenience.h" + +#include <QtGui/QWidget> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +EGLDisplay QBBGLContext::sEglDisplay = EGL_NO_DISPLAY; +EGLConfig QBBGLContext::sEglConfig = 0; +QPlatformWindowFormat QBBGLContext::sWindowFormat; + +QBBGLContext::QBBGLContext(QBBWindow* platformWindow) + : QPlatformGLContext(), + mPlatformWindow(platformWindow), + mEglSurface(EGL_NO_SURFACE) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::QBBGLContext - w=" << mPlatformWindow->widget(); +#endif + + // verify rendering API is correct + QPlatformWindowFormat format = mPlatformWindow->widget()->platformWindowFormat(); + if (format.windowApi() != QPlatformWindowFormat::OpenGL) { + qFatal("QBB: window API is not OpenGL"); + } + + // 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 + int depth = platformWindow->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 + sEglConfig = q_configFromQPlatformWindowFormat(sEglDisplay, format); + if (sEglConfig == 0) { + qFatal("QBB: failed to find EGL config"); + } + + mEglContext = eglCreateContext(sEglDisplay, sEglConfig, EGL_NO_CONTEXT, contextAttrs()); + if (mEglContext == EGL_NO_CONTEXT) { + qFatal("QBB: failed to create EGL context, err=%d", eglGetError()); + } + + // query/cache window format of selected EGL config + sWindowFormat = qt_qPlatformWindowFormatFromConfig(sEglDisplay, sEglConfig); +} + +QBBGLContext::~QBBGLContext() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::~QBBGLContext - w=" << mPlatformWindow->widget(); +#endif + + // cleanup EGL context if it exists + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(sEglDisplay, mEglContext); + } + + // cleanup EGL surface if it exists + destroySurface(); +} + +void QBBGLContext::initialize() +{ + // initialize connection to EGL + sEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (sEglDisplay == EGL_NO_DISPLAY) { + qFatal("QBB: failed to obtain EGL display"); + } + + EGLBoolean eglResult = eglInitialize(sEglDisplay, NULL, NULL); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to initialize EGL display, err=%d", eglGetError()); + } + + // choose EGL settings based on OpenGL version +#if defined(QT_OPENGL_ES_2) + EGLint renderableType = EGL_OPENGL_ES2_BIT; +#else + EGLint renderableType = EGL_OPENGL_ES_BIT; +#endif + + // get EGL config compatible with window + EGLint numConfig; + EGLint configAttrs[] = { EGL_BUFFER_SIZE, 32, + EGL_ALPHA_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, renderableType, + EGL_NONE }; + eglResult = eglChooseConfig(sEglDisplay, configAttrs, &sEglConfig, 1, &numConfig); + if (eglResult != EGL_TRUE || numConfig == 0) { + qFatal("QBB: failed to find EGL config, err=%d, numConfig=%d", eglGetError(), numConfig); + } + + // query/cache window format + sWindowFormat = qt_qPlatformWindowFormatFromConfig(sEglDisplay, sEglConfig); + sWindowFormat.setWindowApi(QPlatformWindowFormat::OpenGL); +} + +void QBBGLContext::shutdown() +{ + // close connection to EGL + eglTerminate(sEglDisplay); +} + +void QBBGLContext::makeCurrent() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::makeCurrent - w=" << mPlatformWindow->widget(); +#endif + + if (!mPlatformWindow->hasBuffers()) { + // NOTE: create a dummy EGL surface since Qt will call makeCurrent() before + // setting the geometry on the associated window + mSurfaceSize = QSize(1, 1); + mPlatformWindow->setBufferSize(mSurfaceSize); + createSurface(); + } + + // call the parent method + QPlatformGLContext::makeCurrent(); + + // 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()); + } + + // set current EGL context and bind with EGL surface + eglResult = eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to set current EGL context, err=%d", eglGetError()); + } +} + +void QBBGLContext::doneCurrent() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::doneCurrent - w=" << mPlatformWindow->widget(); +#endif + + // call the parent method + QPlatformGLContext::doneCurrent(); + + // 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(sEglDisplay, 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() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::swapBuffers - w=" << mPlatformWindow->widget(); +#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(sEglDisplay, mEglSurface); + if (eglResult != EGL_TRUE) { + qFatal("QBB: failed to swap EGL buffers, err=%d", eglGetError()); + } + + // resize surface if window was resized + QSize s = mPlatformWindow->geometry().size(); + if (s != mSurfaceSize) { + resizeSurface(s); + } +} + +void* QBBGLContext::getProcAddress(const QString& procName) +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::getProcAddress - w=" << mPlatformWindow->widget(); +#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 (void *)eglGetProcAddress( procName.toAscii().constData() ); +} + +void QBBGLContext::resizeSurface(const QSize &size) +{ + // need to destroy surface so make sure its not current + bool restoreCurrent = false; + if (isCurrent()) { + doneCurrent(); + restoreCurrent = true; + } + + // destroy old EGL surface + destroySurface(); + + // resize window's buffers + mPlatformWindow->setBufferSize(size); + + // re-create EGL surface with new size + mSurfaceSize = size; + createSurface(); + + // make context current again + if (restoreCurrent) { + makeCurrent(); + } +} + +EGLint *QBBGLContext::contextAttrs() +{ + // 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 NULL; +#endif +} + +bool QBBGLContext::isCurrent() const +{ + return (eglGetCurrentContext() == mEglContext); +} + +void QBBGLContext::createSurface() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::createSurface - w=" << mPlatformWindow->widget(); +#endif + + // create EGL surface + mEglSurface = eglCreateWindowSurface(sEglDisplay, sEglConfig, (EGLNativeWindowType)mPlatformWindow->winId(), NULL); + if (mEglSurface == EGL_NO_SURFACE) { + qFatal("QBB: failed to create EGL surface, err=%d", eglGetError()); + } +} + +void QBBGLContext::destroySurface() +{ +#if defined(QBBGLCONTEXT_DEBUG) + qDebug() << "QBBGLContext::destroySurface - w=" << mPlatformWindow->widget(); +#endif + + // destroy EGL surface if it exists + if (mEglSurface != EGL_NO_SURFACE) { + EGLBoolean eglResult = eglDestroySurface(sEglDisplay, mEglSurface); + 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 0000000..80d4c53 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglcontext.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBGLCONTEXT_H +#define QBBGLCONTEXT_H + +#include <QtGui/QPlatformGLContext> +#include <QtGui/QPlatformWindowFormat> +#include <QSize> + +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBGLContext : public QPlatformGLContext +{ +public: + QBBGLContext(QBBWindow* platformWindow); + virtual ~QBBGLContext(); + + static void initialize(); + static void shutdown(); + + virtual void makeCurrent(); + virtual void doneCurrent(); + virtual void swapBuffers(); + virtual void* getProcAddress(const QString& procName); + + virtual QPlatformWindowFormat platformWindowFormat() const { return sWindowFormat; } + + void resizeSurface(const QSize &size); + QSize surfaceSize() const { return mSurfaceSize; } + +private: + static EGLDisplay sEglDisplay; + static EGLConfig sEglConfig; + static QPlatformWindowFormat sWindowFormat; + + QBBWindow *mPlatformWindow; + EGLContext mEglContext; + EGLSurface mEglSurface; + QSize mSurfaceSize; + + static EGLint *contextAttrs(); + bool isCurrent() const; + void createSurface(); + void destroySurface(); +}; + +QT_END_NAMESPACE + +#endif // QBBGLCONTEXT_H diff --git a/src/plugins/platforms/blackberry/qbbglwindowsurface.cpp b/src/plugins/platforms/blackberry/qbbglwindowsurface.cpp new file mode 100644 index 0000000..b5a7083 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglwindowsurface.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBGLWINDOWSURFACE_DEBUG + + +#include "qbbglwindowsurface.h" +#include "qbbglcontext.h" +#include "qbbwindow.h" + +#include <QtGui/QWidget> +#include <QtOpenGL/private/qgl_p.h> +#include <QGLContext> +#include <QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QBBGLPaintDevice::QBBGLPaintDevice(QBBGLContext* platformGlContext) + : mPlatformGlContext(platformGlContext) +{ + // create GL context from platform GL context + mGlContext = QGLContext::fromPlatformGLContext(mPlatformGlContext); +} + +QBBGLPaintDevice::~QBBGLPaintDevice() +{ + // cleanup GL context + delete mGlContext; +} + +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 mPlatformGlContext->surfaceSize(); +} + +QBBGLWindowSurface::QBBGLWindowSurface(QWidget *window) + : QWindowSurface(window) +{ +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::QBBGLWindowSurface - w=" << window; +#endif + + // extract GL context associated with window + mPlatformGlContext = static_cast<QBBGLContext*>(window->platformWindow()->glContext()); + + // create an OpenGL paint device + mPaintDevice = new QBBGLPaintDevice(mPlatformGlContext); +} + +QBBGLWindowSurface::~QBBGLWindowSurface() +{ +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::~QBBGLWindowSurface - w=" << window(); +#endif + + // cleanup OpenGL paint device + delete mPaintDevice; +} + +void QBBGLWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(widget); + Q_UNUSED(region); + Q_UNUSED(offset); + +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::flush - w=" << window(); +#endif + + // update the display with newly rendered content + mPlatformGlContext->swapBuffers(); +} + +void QBBGLWindowSurface::resize(const QSize &size) +{ +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::resize - w=" << window() << ", s=" << size; +#endif + + // call parent method + QWindowSurface::resize(size); + + // NOTE: defer resizing window buffers until next paint as + // resize() can be called multiple times before a paint occurs +} + +void QBBGLWindowSurface::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::beginPaint - w=" << window(); +#endif + + // resize EGL surface if window surface resized + QSize s = size(); + if (s != mPlatformGlContext->surfaceSize()) { + mPlatformGlContext->resizeSurface(s); + } +} + +void QBBGLWindowSurface::endPaint(const QRegion ®ion) +{ + Q_UNUSED(region); +#if defined(QBBGLWINDOWSURFACE_DEBUG) + qDebug() << "QBBGLWindowSurface::endPaint - w=" << window(); +#endif +} + +QWindowSurface::WindowSurfaceFeatures QBBGLWindowSurface::features() const +{ + // force full frame updates on every paint + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbglwindowsurface.h b/src/plugins/platforms/blackberry/qbbglwindowsurface.h new file mode 100644 index 0000000..5546432 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbglwindowsurface.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBGLWINDOWSURFACE_H +#define QBBGLWINDOWSURFACE_H + +#include <QtGui/private/qwindowsurface_p.h> +#include <QtOpenGL/private/qglpaintdevice_p.h> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +class QGLContext; +class QBBGLContext; +class QBBWindow; + +class QBBGLPaintDevice : public QGLPaintDevice +{ +public: + QBBGLPaintDevice(QBBGLContext* platformGlContext); + virtual ~QBBGLPaintDevice(); + + virtual QPaintEngine *paintEngine() const; + virtual QSize size() const; + virtual QGLContext *context() const { return mGlContext; } + +private: + QBBGLContext* mPlatformGlContext; + QGLContext *mGlContext; +}; + +class QBBGLWindowSurface : public QWindowSurface +{ +public: + QBBGLWindowSurface(QWidget *window); + virtual ~QBBGLWindowSurface(); + + virtual QPaintDevice *paintDevice() { return mPaintDevice; } + virtual void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + virtual void resize(const QSize &size); + virtual void beginPaint(const QRegion ®ion); + virtual void endPaint(const QRegion ®ion); + + virtual QWindowSurface::WindowSurfaceFeatures features() const; + +private: + QBBGLContext* mPlatformGlContext; + QBBGLPaintDevice* mPaintDevice; +}; + +QT_END_NAMESPACE + +#endif // QBBGLWINDOWSURFACE_H diff --git a/src/plugins/platforms/blackberry/qbbinputcontext.cpp b/src/plugins/platforms/blackberry/qbbinputcontext.cpp new file mode 100644 index 0000000..a7c98f1 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef QBB_IMF +#include "qbbinputcontext_imf.cpp" +#else +#include "qbbinputcontext_noimf.cpp" +#endif + diff --git a/src/plugins/platforms/blackberry/qbbinputcontext.h b/src/plugins/platforms/blackberry/qbbinputcontext.h new file mode 100644 index 0000000..9845f1c --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBB_IMF +#include "qbbinputcontext_noimf.h" +#else +#include "qbbinputcontext_imf.h" +#endif diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp b/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp new file mode 100644 index 0000000..95a0441 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_imf.cpp @@ -0,0 +1,1659 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +/* TODO + * Support inputMethodHints to restrict input (needs additional features in IMF). +*/ + +// #define QBBINPUTCONTEXT_DEBUG +// #define QBBINPUTCONTEXT_IMF_EVENT_DEBUG + +#define STRX(x) #x +#define STR(x) STRX(x) +#define TAG __FILE__ "(" STR(__LINE__) ")" << __func__ << ":" + +#include <qbbeventthread.h> +#include <qbbinputcontext.h> +#include <qbbvirtualkeyboard.h> + +#include <QAction> +#include <QCoreApplication> +#include <QDebug> +#include <QInputMethodEvent> +#include <QMutex> +#include <QTextCharFormat> +#include <QVariant> +#include <QVariantHash> +#include <QWaitCondition> + +#include <dlfcn.h> +#include "imf/imf_client.h" +#include "imf/input_control.h" +#include <process.h> +#include <sys/keycodes.h> + +// 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(QString const& 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() + { + mMutex.lock(); + } + + ~ImfEventResult() + { + mMutex.unlock(); + } + + void wait() + { + mWait.wait(&mMutex); + } + + void signal() + { + mWait.wakeAll(); + } + + void setResult(QVariant const& result) + { + mMutex.lock(); + mRetVal = result; + signal(); + mMutex.unlock(); + } + + QVariant& getResult() + { + return mRetVal; + } + +private: + QVariant mRetVal; + QMutex mMutex; + QWaitCondition mWait; +}; + +class ImfEvent : public QEvent +{ + public: + ImfEvent(input_session_t* session, ImfEventType type, ImfEventResult* result) : + QEvent((QEvent::Type)sUserEventType), + mSession(session), + mImfType(type), + mResult(result) + { + } + ~ImfEvent() { } + + input_session_t* mSession; + ImfEventType mImfType; + QVariantHash mArgs; + ImfEventResult *mResult; + + static int sUserEventType; +}; +int ImfEvent::sUserEventType = QEvent::registerEventType(); + +static int32_t imfBeginBatchEdit(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().toInt(); + + return ret; +} + +static int32_t imfClearMetaKeyStates(input_session_t* ic, int32_t states) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().toInt(); + + 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() << TAG; +#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.getResult().toInt(); + + 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() << TAG; +#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.getResult().toInt(); + + return ret; +} + +static int32_t imfEndBatchEdit(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().toInt(); + + return ret; +} + +static int32_t imfFinishComposingText(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().toInt(); + + return ret; +} + +static int32_t imfGetCursorCapsMode(input_session_t* ic, int32_t req_modes) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().value<int>(); + return ret; +} + +static int32_t imfGetCursorPosition(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().toInt(); + + 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() << TAG; +#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(); + void* ret = result.getResult().value<void*>(); + return (extracted_text_t*)ret; +} + +static spannable_string_t* imfGetSelectedText(input_session_t* ic, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + ImfEventResult result; + ImfEvent* event = new ImfEvent(ic, ImfGetSelectedText, &result); + iarg(flags); + + QCoreApplication::postEvent(QCoreApplication::instance(), event); + + result.wait(); + void* ret = result.getResult().value<void*>(); + return (spannable_string_t*)ret; +} + +static spannable_string_t* imfGetTextAfterCursor(input_session_t* ic, int32_t n, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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(); + void* ret = result.getResult().value<void*>(); + return (spannable_string_t*)ret; +} + +static spannable_string_t* imfGetTextBeforeCursor(input_session_t* ic, int32_t n, int32_t flags) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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(); + void* ret = result.getResult().value<void*>(); + return (spannable_string_t*)ret; +} + +static int32_t imfPerformEditorAction(input_session_t* ic, int32_t editor_action) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().value<int>(); + return ret; +} + +static int32_t imfReportFullscreenMode(input_session_t* ic, int32_t enabled) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().value<int>(); + return ret; +} + +static int32_t imfSendEvent(input_session_t* ic, event_t * event) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEvent* imfEvent = new ImfEvent(ic, ImfSendEvent, 0); + imfEvent->mArgs[qs("event")] = QVariant::fromValue((void*)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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + ImfEvent* imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0); + imfEvent->mArgs[qs("event")] = QVariant::fromValue((void*)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() << TAG; +#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.getResult().value<int>(); + 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() << TAG; +#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.getResult().value<int>(); + return ret; +} + +static int32_t imfSetSelection(input_session_t* ic, int32_t start, int32_t end) +{ +#if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG) + qDebug() << TAG; +#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.getResult().value<int>(); + 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(QString const& text) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << text; +#endif + + spannable_string_t *pString = (spannable_string_t *)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; + + QChar const* 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() << TAG << "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() << TAG << "libinput_client.so.1 did not contain the correct symbols, library mismatch? IMF services are disabled."; + return false; + } + } + + return s_imfReady; +} + +QBBInputContext::QBBInputContext(QObject* parent): + QInputContext(parent), + mLastCaretPos(0), + mIsComposing(false) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#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(); +} + +QBBInputContext::~QBBInputContext() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!imfAvailable()) + return; + + QCoreApplication::instance()->removeEventFilter(this); + p_imf_client_disconnect(); +} + +#define getarg(type, name) type name = imfEvent->mArgs[qs(#name)].value<type>() +#define getparg(type, name) type name = (type)(imfEvent->mArgs[qs(#name)].value<void*>()) + +bool QBBInputContext::eventFilter(QObject *obj, QEvent *event) +{ + + if (event->type() == ImfEvent::sUserEventType) { + // Forward the event to our real handler. + ImfEvent* imfEvent = static_cast<ImfEvent*>(event); + switch (imfEvent->mImfType) { + case ImfBeginBatchEdit: + { + int32_t ret = onBeginBatchEdit(imfEvent->mSession); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfClearMetaKeyStates: + { + getarg(int32_t, states); + int32_t ret = onClearMetaKeyStates(imfEvent->mSession, states); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfCommitText: + { + getparg(spannable_string_t*, text); + getarg(int32_t, new_cursor_position); + int32_t ret = onCommitText(imfEvent->mSession, text, new_cursor_position); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfDeleteSurroundingText: + { + getarg(int32_t, left_length); + getarg(int32_t, right_length); + int32_t ret = onDeleteSurroundingText(imfEvent->mSession, left_length, right_length); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfEndBatchEdit: + { + int32_t ret = onEndBatchEdit(imfEvent->mSession); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfFinishComposingText: + { + int32_t ret = onFinishComposingText(imfEvent->mSession); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetCursorCapsMode: + { + getarg(int32_t, req_modes); + int32_t ret = onGetCursorCapsMode(imfEvent->mSession, req_modes); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetCursorPosition: + { + int32_t ret = onGetCursorPosition(imfEvent->mSession); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfGetExtractedText: + { + getparg(extracted_text_request_t*, request); + getarg(int32_t, flags); + extracted_text_t* ret = onGetExtractedText(imfEvent->mSession, request, flags); + imfEvent->mResult->setResult(QVariant::fromValue((void*)ret)); + break; + } + + case ImfGetSelectedText: + { + getarg(int32_t, flags); + spannable_string_t* ret = onGetSelectedText(imfEvent->mSession, flags); + imfEvent->mResult->setResult(QVariant::fromValue((void*)ret)); + break; + } + + case ImfGetTextAfterCursor: + { + getarg(int32_t, n); + getarg(int32_t, flags); + spannable_string_t* ret = onGetTextAfterCursor(imfEvent->mSession, n, flags); + imfEvent->mResult->setResult(QVariant::fromValue((void*)ret)); + break; + } + + case ImfGetTextBeforeCursor: + { + getarg(int32_t, n); + getarg(int32_t, flags); + spannable_string_t* ret = onGetTextBeforeCursor(imfEvent->mSession, n, flags); + imfEvent->mResult->setResult(QVariant::fromValue((void*)ret)); + break; + } + + case ImfPerformEditorAction: + { + getarg(int32_t, editor_action); + int32_t ret = onPerformEditorAction(imfEvent->mSession, editor_action); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfReportFullscreenMode: + { + getarg(int32_t, enabled); + int32_t ret = onReportFullscreenMode(imfEvent->mSession, enabled); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSendEvent: + { + getparg(event_t*, event); + onSendEvent(imfEvent->mSession, event); + break; + } + + case ImfSendAsyncEvent: + { + getparg(event_t*, event); + onSendAsyncEvent(imfEvent->mSession, event); + break; + } + + case ImfSetComposingRegion: + { + getarg(int32_t, start); + getarg(int32_t, end); + int32_t ret = onSetComposingRegion(imfEvent->mSession, start, end); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSetComposingText: + { + getparg(spannable_string_t*, text); + getarg(int32_t, new_cursor_position); + int32_t ret = onSetComposingText(imfEvent->mSession, text, new_cursor_position); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + + case ImfSetSelection: + { + getarg(int32_t, start); + getarg(int32_t, end); + int32_t ret = onSetSelection(imfEvent->mSession, start, end); + imfEvent->mResult->setResult(QVariant::fromValue(ret)); + break; + } + }; //switch + + return true; + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } +} + +QString QBBInputContext::identifierName() +{ + return tr("PlayBook IMF"); +} + +QString QBBInputContext::language() +{ + return QBBVirtualKeyboard::instance().languageId(); +} + +bool QBBInputContext::filterEvent( const QEvent *event ) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << event; +#endif + + switch (event->type()) { + case QEvent::CloseSoftwareInputPanel: { + return dispatchCloseSoftwareInputPanel(); + } + case QEvent::RequestSoftwareInputPanel: { + return dispatchRequestSoftwareInputPanel(); + } + default: + return false; + } +} + +QList<QAction *> QBBInputContext::actions() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + return QInputContext::actions(); +} + +QFont QBBInputContext::font() const +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + return QInputContext::font(); +} + +bool QBBInputContext::isComposing() const +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + return mIsComposing; +} + +void QBBInputContext::mouseHandler(int x, QMouseEvent * event) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + return QInputContext::mouseHandler(x, event); +} + +void QBBInputContext::reset() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + endComposition(); +} + +void QBBInputContext::setFocusWidget(QWidget * widget) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (hasSession()) { + endComposition(); + dispatchFocusEvent(FOCUS_LOST); + closeSession(); + } + + // Update the widget before moving on. + QInputContext::setFocusWidget(widget); + + // If we have hidden text, or any flags that restrict input (exclusive flags), then we just disable + // imf for this field. + if (widget != 0 && !(widget->inputMethodHints() & Qt::ImhHiddenText) && !(widget->inputMethodHints() >= Qt::ImhDigitsOnly && widget->inputMethodHints() <= Qt::ImhUrlCharactersOnly)) + { +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << "Starting input session for " << widget; +#endif + openSession(); + dispatchFocusEvent(FOCUS_GAINED, widget->inputMethodHints()); + } +} + +void QBBInputContext::update() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + reset(); + + QInputContext::update(); +} + +void QBBInputContext::widgetDestroyed(QWidget * widget) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + QInputContext::widgetDestroyed(widget); +} + +void QBBInputContext::closeSession() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG;; +#endif + + if (!imfAvailable()) + return; + + if (sInputSession) { + p_ictrl_close_session((input_session_t *)sInputSession); + sInputSession = 0; + } +} + +void QBBInputContext::openSession() +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG;; +#endif + + if (!imfAvailable()) + return; + + closeSession(); + sInputSession = p_ictrl_open_session(&ic_funcs); +} + +bool QBBInputContext::hasSession() +{ + return sInputSession != 0; +} + +bool QBBInputContext::hasSelectedText() +{ + if (focusWidget()) { + return focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt() != focusWidget()->inputMethodQuery(Qt::ImAnchorPosition).toInt(); + } + else + { + return false; + } +} + +bool QBBInputContext::dispatchRequestSoftwareInputPanel() +{ + QBBVirtualKeyboard::instance().showKeyboard(); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << "QBB: requesting virtual keyboard"; +#endif + + if (!imfAvailable() || !focusWidget()) + return true; + + // This also means that the caret position has moved + int caretPos = focusWidget()->inputMethodQuery(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 = mLastCaretPos; + mLastCaretPos = 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() << TAG; +#endif + + if (!sInputSession) { + qWarning() << TAG << "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. + mLastCaretPos = 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() << TAG << "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() << TAG << "dispatch key event " << key; +#endif + } + + scan = 0; + return true; +} + +void QBBInputContext::endComposition() +{ + if (!imfAvailable()) + return; + + if (!isComposing()) + return; + + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(mComposingText); + mComposingText = QString(); + mIsComposing = false; + sendEvent(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) +{ + mComposingText = composingText; + mIsComposing = true; + + QList<QInputMethodEvent::Attribute> attributes; + QTextCharFormat format; + format.setFontUnderline(true); + attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format)); + + QInputMethodEvent event(composingText, attributes); + + sendEvent(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() << TAG << "EVENT_SPELL_CHECK"; + #endif + result = 0; + break; + } + + case EVENT_NAVIGATION: { + #if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << "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() << TAG << "EVENT_KEY"; + #endif + key_event_t* kevent = (key_event_t*) 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() << TAG << "Unsupported event type: " << event->event_type; + break; + default: + qCritical() << TAG << "Unknown event type: " << event->event_type; + } + return result; +} + +/* + * IMF Event Handlers + */ + +int32_t QBBInputContext::onBeginBatchEdit(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << TAG << "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; + + QString commitString = QString::fromWCharArray(text->str, text->length); + +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << "Committing [" << commitString << "]"; +#endif + + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(commitString, 0, 0); + + sendEvent(event); + mComposingText = QString(); + + return 0; +} + +int32_t QBBInputContext::onDeleteSurroundingText(input_session_t* ic, int32_t left_length, int32_t right_length) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG << "L:" << left_length << " R:" << right_length; +#endif + + if (!isSessionOkay(ic)) + 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<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(QLatin1String(""), replacementStart, replacementLength); + sendEvent(event); + + return 0; +} + +int32_t QBBInputContext::onEndBatchEdit(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + return 0; +} + +int32_t QBBInputContext::onFinishComposingText(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Only update the control, no need to send a message back to imf (don't call + // end composition) + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event(QLatin1String(""), attributes); + event.setCommitString(mComposingText); + mComposingText = QString(); + mIsComposing = false; + sendEvent(event); + + return 0; +} + +int32_t QBBInputContext::onGetCursorCapsMode(input_session_t* ic, int32_t req_modes) +{ + Q_UNUSED(req_modes); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << TAG << "onGetCursorCapsMode is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onGetCursorPosition(input_session_t* ic) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + return mLastCaretPos; +} + +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() << TAG; +#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 0; + } + + // Used to update dictionaries, but not supported right now. + 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; +} + +spannable_string_t* QBBInputContext::onGetSelectedText(input_session_t* ic, int32_t flags) +{ + Q_UNUSED(flags); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QString text = focusWidget()->inputMethodQuery(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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); + mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + + return toSpannableString(text.mid(mLastCaretPos+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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return toSpannableString(""); + + QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); + mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + + if (n < mLastCaretPos) + { + return toSpannableString(text.mid(mLastCaretPos - n, n)); + } + else + return toSpannableString(text.mid(0, mLastCaretPos)); +} + +int32_t QBBInputContext::onPerformEditorAction(input_session_t* ic, int32_t editor_action) +{ + Q_UNUSED(editor_action); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << TAG << "onPerformEditorAction is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onReportFullscreenMode(input_session_t* ic, int32_t enabled) +{ + Q_UNUSED(enabled); +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << TAG << "onReportFullscreenMode is unsupported."; + + return 0; +} + +int32_t QBBInputContext::onSendEvent(input_session_t* ic, event_t * event) +{ +#if defined(QBBINPUTCONTEXT_DEBUG) + qDebug() << TAG; +#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() << TAG; +#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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + if (!focusWidget()) + { + qCritical() << "No focus widget!"; + return 0; + } + + QList<QInputMethodEvent::Attribute> attributes; + + mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); + QString empty = QString::fromLatin1(""); + text = text.mid(start, end - start); + + // Delete the current text. + { + QInputMethodEvent event(empty, attributes); + event.setCommitString(empty, start - mLastCaretPos, end - start); + sendEvent(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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + if (!focusWidget()) + { + qCritical() << "No focus widget!"; + return 0; + } + + mIsComposing = 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() << TAG; +#endif + + if (!isSessionOkay(ic)) + return 0; + + // Should never get called. + qCritical() << TAG << "onSetSelection is unsupported."; + + return 0; +} + + diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_imf.h b/src/plugins/platforms/blackberry/qbbinputcontext_imf.h new file mode 100644 index 0000000..6d18ddd --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_imf.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINPUTCONTEXT_H +#define QBBINPUTCONTEXT_H + +#include "imf/imf_client.h" +#include "imf/input_control.h" + +#include <QtGui/QPlatformIntegration> +#include <qinputcontext.h> + +QT_BEGIN_NAMESPACE + +class QBBInputContext : public QInputContext +{ +public: + explicit QBBInputContext(QObject* parent = 0); + ~QBBInputContext(); + + virtual QList<QAction *> actions(); + virtual bool filterEvent(const QEvent * event); + virtual QFont font() const; + virtual QString identifierName(); + virtual bool isComposing() const; + virtual QString language(); + virtual void mouseHandler(int x, QMouseEvent * event); + virtual void reset(); + virtual void setFocusWidget(QWidget * widget); + virtual void update(); + virtual void widgetDestroyed(QWidget * widget); + bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + +protected: + // Filters only for IMF events. + bool eventFilter(QObject *obj, QEvent *event); + +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 mLastCaretPos; + bool mIsComposing; + QString mComposingText; +}; + +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 0000000..8894098 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBINPUTCONTEXT_DEBUG + +#include <qbbinputcontext.h> +#include <qbbvirtualkeyboard.h> + +#include <QDebug> + +QBBInputContext::QBBInputContext(QObject* parent): + QInputContext(parent) +{ +} + +QBBInputContext::~QBBInputContext() +{ +} + +QString QBBInputContext::language() +{ + // Once we enable full IMF support, we need to hook that up here. + return QBBVirtualKeyboard::instance().languageId(); +} + +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) +{ + return false; +} + diff --git a/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h new file mode 100644 index 0000000..d7f3cc5 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbinputcontext_noimf.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINPUTCONTEXT_H +#define QBBINPUTCONTEXT_H + +#include <QtGui/QPlatformIntegration> +#include <qinputcontext.h> + +QT_BEGIN_NAMESPACE + +class QBBInputContext : public QInputContext +{ +public: + explicit QBBInputContext(QObject* parent = 0); + ~QBBInputContext(); + QString identifierName() { return QString("BlackBerry IMF"); } + QString language(); + + void reset(); + bool isComposing() const { return false; } + virtual bool filterEvent( const QEvent *event ); + bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + +private: + bool hasPhysicalKeyboard(); + +}; + +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 0000000..cc16938 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbintegration.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBINTEGRATION_DEBUG + +#include "qbbintegration.h" +#include "qbbinputcontext.h" +#include "qbbeventthread.h" +#include "qbbglcontext.h" +#include "qbbglwindowsurface.h" +#include "qbbnavigatorthread.h" +#include "qbbrasterwindowsurface.h" +#include "qbbscreen.h" +#include "qbbwindow.h" +#include "qbbvirtualkeyboard.h" +#include "qgenericunixfontdatabase.h" +#include "qbbclipboard.h" +#include "qbbglcontext.h" +#include "qbblocalethread.h" + +#include "qapplication.h" +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/QPlatformWindow> +#include <QtGui/QWindowSystemInterface> +#include <QtOpenGL/private/qpixmapdata_gl_p.h> +#include <QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QBBIntegration::QBBIntegration() : + mFontDb(new QGenericUnixFontDatabase()), + mPaintUsingOpenGL(getenv("QBB_USE_OPENGL") != NULL) +{ + if (mPaintUsingOpenGL) { + // Set default window API to OpenGL + QPlatformWindowFormat format = QPlatformWindowFormat::defaultFormat(); + format.setWindowApi(QPlatformWindowFormat::OpenGL); + QPlatformWindowFormat::setDefaultFormat(format); + } + + // initialize global OpenGL resources + QBBGLContext::initialize(); + + // open connection to QNX composition manager + errno = 0; + int result = screen_create_context(&mContext, 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(mContext); + + // create/start event thread + mEventThread = new QBBEventThread(mContext, *QBBScreen::primaryDisplay()); + mEventThread->start(); + + // create/start navigator thread + mNavigatorThread = new QBBNavigatorThread(*QBBScreen::primaryDisplay()); + mNavigatorThread->start(); + +#ifdef QBBLOCALETHREAD_ENABLED + // Start the locale change monitoring thread. + mLocaleThread = new QBBLocaleThread(); + mLocaleThread->start(); +#endif + + // create/start the keyboard class. + QBBVirtualKeyboard::instance(); + + // Set up the input context + qApp->setInputContext(new QBBInputContext(qApp)); +} + +QBBIntegration::~QBBIntegration() +{ +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << "QBB: platform plugin shutdown begin"; +#endif + // destroy the keyboard class. + QBBVirtualKeyboard::destroy(); + +#ifdef QBBLOCALETHREAD_ENABLED + // stop/destroy the locale thread. + delete mLocaleThread; +#endif + + // stop/destroy event thread + delete mEventThread; + + // stop/destroy navigator thread + delete mNavigatorThread; + + // destroy all displays + QBBScreen::destroyDisplays(); + + // close connection to QNX composition manager + screen_destroy_context(mContext); + + // cleanup global OpenGL resources + QBBGLContext::shutdown(); + +#if defined(QBBINTEGRATION_DEBUG) + qDebug() << "QBB: platform plugin shutdown end"; +#endif +} + +bool QBBIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; +#if defined(QT_OPENGL_ES) + case OpenGL: return true; +#endif + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPixmapData *QBBIntegration::createPixmapData(QPixmapData::PixelType type) const +{ + if (paintUsingOpenGL()) + return new QGLPixmapData(type); + else + return new QRasterPixmapData(type); +} + +QPlatformWindow *QBBIntegration::createPlatformWindow(QWidget *widget, WId winId) const +{ + Q_UNUSED(winId); + + // New windows are created on the primary display. + return new QBBWindow(widget, mContext); +} + +QWindowSurface *QBBIntegration::createWindowSurface(QWidget *widget, WId winId) const +{ + Q_UNUSED(winId); + if (paintUsingOpenGL()) + return new QBBGLWindowSurface(widget); + else + return new QBBRasterWindowSurface(widget); +} + +void QBBIntegration::moveToScreen(QWidget *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<QBBWindow*>(window->platformWindow()); + + // lookup platform screen by index + QBBScreen* platformScreen = static_cast<QBBScreen*>(QBBScreen::screens().at(screen)); + + // move the platform window to the platform screen (this can fail when move to screen + // is called before the window is actually created). + if (platformWindow && platformScreen) + platformWindow->setScreen(platformScreen); +} + +QList<QPlatformScreen *> QBBIntegration::screens() const +{ + return QBBScreen::screens(); +} + +#ifndef QT_NO_CLIPBOARD +QPlatformClipboard *QBBIntegration::clipboard() const +{ + static QPlatformClipboard *clipboard = 0; + if (!clipboard) { + clipboard = static_cast<QPlatformClipboard *>(new QBBClipboard); + } + return clipboard; +} +#endif + +void QBBIntegration::setCursorPos(int x, int y) +{ + mEventThread->injectPointerMoveEvent(x, y); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbintegration.h b/src/plugins/platforms/blackberry/qbbintegration.h new file mode 100644 index 0000000..8c20b6c --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbintegration.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBINTEGRATION_H +#define QBBINTEGRATION_H + +#include <QtGui/QPlatformIntegration> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +class QBBEventThread; +class QBBNavigatorThread; +class QBBLocaleThread; + +class QBBIntegration : public QPlatformIntegration +{ +public: + QBBIntegration(); + virtual ~QBBIntegration(); + + virtual bool hasCapability(QPlatformIntegration::Capability cap) const; + + virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + virtual QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; + virtual QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + + virtual QList<QPlatformScreen *> screens() const; + virtual void moveToScreen(QWidget *window, int screen); + virtual void setCursorPos(int x, int y); + + virtual QPlatformFontDatabase *fontDatabase() const { return mFontDb; } + +#ifndef QT_NO_CLIPBOARD + virtual QPlatformClipboard *clipboard() const; +#endif + + bool paintUsingOpenGL() const { return mPaintUsingOpenGL; } + +private: + screen_context_t mContext; + QBBEventThread *mEventThread; + QBBNavigatorThread *mNavigatorThread; + QBBLocaleThread *mLocaleThread; + QPlatformFontDatabase *mFontDb; + bool mPaintUsingOpenGL; +}; + +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 0000000..88c9660 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbkeytranslator.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBKEYTRANSLATOR_H +#define QBBKEYTRANSLATOR_H + +#include <sys/keycodes.h> +#include <QDebug> + +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/qbblocalethread.cpp b/src/plugins/platforms/blackberry/qbblocalethread.cpp new file mode 100644 index 0000000..1da94e2 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbblocalethread.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBLOCALETHREAD_DEBUG + +#ifdef QBBLOCALETHREAD_ENABLED + +#include "qbblocalethread.h" + +#include <QtGui/QApplication> +#include <QtGui/QWidget> +#include <QtGui/QWindowSystemInterface> +#include <QByteArray> +#include <QList> +#include <QDebug> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#define NAV_CONTROL_PATH "/pps/services/confstr/_CS_LOCALE" +#define PPS_BUFFER_SIZE 4096 + +QBBLocaleThread::QBBLocaleThread() + : mFd(-1), + mQuit(false) +{ +} + +QBBLocaleThread::~QBBLocaleThread() +{ + // block until thread terminates + shutdown(); +} + +void QBBLocaleThread::run() +{ +#if defined(QBBLOCALETHREAD_DEBUG) + qDebug() << "QBB: Locale thread started"; +#endif + + // open connection to Locale + errno = 0; + mFd = open(NAV_CONTROL_PATH, O_RDWR); + if (mFd == -1) { + qWarning("QBB: failed to open Locale pps, errno=%d", errno); + return; + } + + // allocate buffer for pps data + char buffer[PPS_BUFFER_SIZE]; + + // loop indefinitely + while (!mQuit) { + + // attempt to read pps data + errno = 0; + int bytes = read(mFd, buffer, PPS_BUFFER_SIZE - 1); + if (bytes == -1) { + qWarning("QBB: failed to read Locale pps, errno=%d", errno); + return; + } + + // check if pps data was received + if (bytes > 0) { + // Notify the world that the locale has changed. + QWindowSystemInterface::handleLocaleChange(); + } + + // yield + msleep(5); + } + + // close connection to Locale + close(mFd); + +#if defined(QBBLOCALETHREAD_DEBUG) + qDebug() << "QBB: Locale thread stopped"; +#endif +} + +void QBBLocaleThread::shutdown() +{ + // signal thread to terminate + mQuit = true; + +#if defined(QBBLOCALETHREAD_DEBUG) + qDebug() << "QBB: Locale thread shutdown begin"; +#endif + + // block until thread terminates + wait(); + +#if defined(QBBLOCALETHREAD_DEBUG) + qDebug() << "QBB: Locale thread shutdown end"; +#endif +} + +#endif // QBBLOCALETHREAD_ENABLED diff --git a/src/plugins/platforms/blackberry/qbblocalethread.h b/src/plugins/platforms/blackberry/qbblocalethread.h new file mode 100644 index 0000000..8a0413f --- /dev/null +++ b/src/plugins/platforms/blackberry/qbblocalethread.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef QBBLOCALETHREAD_ENABLED + +#ifndef QBBLOCALETHREAD_H +#define QBBLOCALETHREAD_H + +#include <QThread> + +QT_BEGIN_NAMESPACE + +class QBBLocaleThread : public QThread +{ +public: + QBBLocaleThread(); + virtual ~QBBLocaleThread(); + +protected: + virtual void run(); + +private: + int mFd; + bool mQuit; + + void shutdown(); +}; + +QT_END_NAMESPACE + +#endif // QBBLOCALETHREAD_H + +#endif // QBBLOCALETHREAD_ENABLED diff --git a/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp b/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp new file mode 100644 index 0000000..97d50a9 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbnavigatorthread.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBNAVIGATORTHREAD_DEBUG + + +#include "qbbnavigatorthread.h" +#include "qbbscreen.h" + +#include <QtGui/QApplication> +#include <QtGui/QWidget> +#include <QtGui/QWindowSystemInterface> +#include <QByteArray> +#include <QList> +#include <QDebug> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#define NAV_CONTROL_PATH "/pps/services/navigator/control" +#define PPS_BUFFER_SIZE 4096 + +QBBNavigatorThread::QBBNavigatorThread(QBBScreen& primaryScreen) + : mPrimaryScreen(primaryScreen), + mFd(-1), + mQuit(false) +{ +} + +QBBNavigatorThread::~QBBNavigatorThread() +{ + // block until thread terminates + shutdown(); +} + +void QBBNavigatorThread::run() +{ +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread started"; +#endif + + // open connection to navigator + errno = 0; + mFd = open(NAV_CONTROL_PATH, O_RDWR); + if (mFd == -1) { + qWarning("QBB: failed to open navigator pps, errno=%d", errno); + return; + } + + // allocate buffer for pps data + char buffer[PPS_BUFFER_SIZE]; + + // loop indefinitely + while (!mQuit) { + + // attempt to read pps data + errno = 0; + int bytes = read(mFd, buffer, PPS_BUFFER_SIZE - 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); + } + + // yield + msleep(5); + } + + // close connection to navigator + close(mFd); + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread stopped"; +#endif +} + +void QBBNavigatorThread::shutdown() +{ + // signal thread to terminate + mQuit = true; + +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "QBB: navigator thread shutdown begin"; +#endif + + // 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<QByteArray> 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(mFd, 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 + mPrimaryScreen.setRotation( dat.toInt() ); + QWindowSystemInterface::handleScreenGeometryChange(0); + replyPPS(msg, id, ""); + + } else if (msg == "SWIPE_DOWN") { + + // simulate menu key press +#if defined(QBBNAVIGATORTHREAD_DEBUG) + qDebug() << "PPS: menu"; +#endif + QWidget *w = QApplication::activeWindow(); + 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 + QApplication::quit(); + } +} diff --git a/src/plugins/platforms/blackberry/qbbnavigatorthread.h b/src/plugins/platforms/blackberry/qbbnavigatorthread.h new file mode 100644 index 0000000..fa7e8ae --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbnavigatorthread.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBNAVIGATORTHREAD_H +#define QBBNAVIGATORTHREAD_H + +#include <QThread> + +QT_BEGIN_NAMESPACE + +class QBBScreen; + +class QBBNavigatorThread : public QThread +{ +public: + QBBNavigatorThread(QBBScreen& primaryScreen); + virtual ~QBBNavigatorThread(); + +protected: + virtual void run(); + +private: + QBBScreen& mPrimaryScreen; + int mFd; + bool mQuit; + + 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); +}; + +QT_END_NAMESPACE + +#endif // QBBNAVIGATORTHREAD_H diff --git a/src/plugins/platforms/blackberry/qbbrasterwindowsurface.cpp b/src/plugins/platforms/blackberry/qbbrasterwindowsurface.cpp new file mode 100644 index 0000000..548ca4d --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrasterwindowsurface.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBRASTERWINDOWSURFACE_DEBUG + + +#include "qbbrasterwindowsurface.h" +#include "qbbwindow.h" + +#include <QtGui/QWidget> +#include <QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QBBRasterWindowSurface::QBBRasterWindowSurface(QWidget *window) + : QWindowSurface(window) +{ +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::QBBRasterWindowSurface - w=" << window; +#endif + + // save platform window associated with widget + mPlatformWindow = static_cast<QBBWindow*>(window->platformWindow()); +} + +QBBRasterWindowSurface::~QBBRasterWindowSurface() +{ +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::~QBBRasterWindowSurface - w=" << window(); +#endif +} + +QPaintDevice *QBBRasterWindowSurface::paintDevice() +{ + return mPlatformWindow->renderBuffer().image(); +} + +void QBBRasterWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(widget); + Q_UNUSED(offset); + +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::flush - w=" << window(); +#endif + + // visit all pending scroll operations + for (int i = mScrollOpList.size() - 1; i >= 0; i--) { + + // do the scroll operation + ScrollOp& op = mScrollOpList[i]; + QRegion srcArea = op.totalArea.intersected( op.totalArea.translated(-op.dx, -op.dy) ); + mPlatformWindow->scroll(srcArea, op.dx, op.dy); + } + + // clear all pending scroll operations + mScrollOpList.clear(); + + // update the display with newly rendered content + mPlatformWindow->post(region); +} + +void QBBRasterWindowSurface::resize(const QSize &size) +{ +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::resize - w=" << window() << ", s=" << size; +#endif + + // call parent method + QWindowSurface::resize(size); + + // NOTE: defer resizing window buffers until next paint as + // resize() can be called multiple times before a paint occurs +} + +bool QBBRasterWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::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 = mScrollOpList.size() - 1; i >= 0; i--) { + + ScrollOp& op = mScrollOpList[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 + mScrollOpList.append( ScrollOp(totalArea, dx, dy) ); + return true; +} + +void QBBRasterWindowSurface::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::beginPaint - w=" << window(); +#endif + + // resize window buffers if surface resized + QSize s = size(); + if (s != mPlatformWindow->bufferSize()) { + mPlatformWindow->setBufferSize(s); + } +} + +void QBBRasterWindowSurface::endPaint(const QRegion ®ion) +{ + Q_UNUSED(region); +#if defined(QBBRASTERWINDOWSURFACE_DEBUG) + qDebug() << "QBBRasterWindowSurface::endPaint - w=" << window(); +#endif +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbrasterwindowsurface.h b/src/plugins/platforms/blackberry/qbbrasterwindowsurface.h new file mode 100644 index 0000000..02c8eb3 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbrasterwindowsurface.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBRASTERWINDOWSURFACE_H +#define QBBRASTERWINDOWSURFACE_H + +#include <QtGui/private/qwindowsurface_p.h> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBRasterWindowSurface : public QWindowSurface +{ +public: + QBBRasterWindowSurface(QWidget *window); + virtual ~QBBRasterWindowSurface(); + + virtual QPaintDevice *paintDevice(); + virtual void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + virtual void resize(const QSize &size); + 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 *mPlatformWindow; + QList<ScrollOp> mScrollOpList; +}; + +QT_END_NAMESPACE + +#endif // QBBRASTERWINDOWSURFACE_H diff --git a/src/plugins/platforms/blackberry/qbbscreen.cpp b/src/plugins/platforms/blackberry/qbbscreen.cpp new file mode 100644 index 0000000..31e3f43 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbscreen.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBSCREEN_DEBUG + +#include "qbbscreen.h" +#include "qbbvirtualkeyboard.h" +#include "qbbwindow.h" + +#include <QUuid> +#include <QDebug> + +#include <errno.h> +#include <unistd.h> + +QT_BEGIN_NAMESPACE + +#define MAGIC_ZORDER_FOR_NO_NAV 10 + +QList<QPlatformScreen *> QBBScreen::sScreens; +QList<QBBWindow*> QBBScreen::sChildren; + +QBBScreen::QBBScreen(screen_context_t context, screen_display_t display, bool primary) + : mContext(context), + mDisplay(display), + mAppWindow(0), + mPosted(false), + mUsingOpenGL(false), + mPrimaryDisplay(primary) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::QBBScreen"; +#endif + + // cache initial orientation of this display + // TODO: use ORIENTATION environment variable? + errno = 0; + int result = screen_get_display_property_iv(mDisplay, SCREEN_PROPERTY_ROTATION, &mStartRotation); + if (result != 0) { + qFatal("QBBScreen: failed to query display rotation, errno=%d", errno); + } + + mCurrentRotation = mStartRotation; + + // 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(mDisplay, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to query display size, errno=%d", errno); + } + + mCurrentGeometry = mStartGeometry = QRect(0, 0, val[0], val[1]); + + // cache size of this display in millimeters + errno = 0; + result = screen_get_display_property_iv(mDisplay, 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 = mCurrentGeometry.width() / dpi * qreal(25.4) ; + int height = mCurrentGeometry.height() / dpi * qreal(25.4) ; + + mCurrentPhysicalSize = mStartPhysicalSize = QSize(width,height); + } + + // We only create the root window if we are not the primary display. + if (mPrimaryDisplay) + createRootWindow(); +} + +QBBScreen::~QBBScreen() +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::~QBBScreen"; +#endif + + destroyRootWindow(); +} + +/* static */ +void QBBScreen::createDisplays(screen_context_t context) +{ + // get 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<displayCount; i++) { +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::Creating screen for display " << i; +#endif + QBBScreen *screen = new QBBScreen(context, displays[i], i==0); + sScreens.push_back(screen); + } +} + +/* static */ +void QBBScreen::destroyDisplays() +{ + while (sScreens.length()) { + delete sScreens.front(); + sScreens.pop_front(); + } + + // We're not managing the child windows anymore so we need to clear the list. + sChildren.clear(); +} + +/* static */ +int QBBScreen::defaultDepth() +{ + static int defaultDepth = 0; + if (defaultDepth == 0) { + // check if display depth was specified in environment variable; + // use default value if no valid value found + defaultDepth = qgetenv("QBB_DISPLAY_DEPTH").toInt(); + if (defaultDepth != 16 && defaultDepth != 32) { + defaultDepth = 32; + } + } + return defaultDepth; +} + +void QBBScreen::ensureDisplayCreated() +{ + if (!mAppWindow) + createRootWindow(); +} + +void QBBScreen::createRootWindow() +{ + // 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(&mAppWindow, mContext); + int val[2]; + if (result != 0) { + qFatal("QBBScreen: failed to create window, errno=%d", errno); + } + + // move window to proper display + errno = 0; + result = screen_set_window_property_pv(mAppWindow, SCREEN_PROPERTY_DISPLAY, (void **)&mDisplay); + if (result != 0) { + qFatal("QBBScreen: 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(mAppWindow, SCREEN_PROPERTY_ZORDER, val); + if (result != 0) { + qFatal("QBBScreen: 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(mAppWindow, SCREEN_PROPERTY_USAGE, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window buffer usage, errno=%d", errno); + } + + errno = 0; + val[0] = nativeFormat(); + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_FORMAT, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window pixel format, errno=%d", errno); + } + + errno = 0; + val[0] = 1; + val[1] = 1; + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_BUFFER_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window buffer size, errno=%d", errno); + } + + errno = 0; + result = screen_create_window_buffers(mAppWindow, 1); + if (result != 0) { + qFatal("QBB: failed to create window buffer, errno=%d", errno); + } + + // window is always the size of the display + errno = 0; + val[0] = mCurrentGeometry.width(); + val[1] = mCurrentGeometry.height(); + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: 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(mAppWindow, SCREEN_PROPERTY_COLOR, val); + if (result != 0) { + qFatal("QBBScreen: 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(mAppWindow, SCREEN_PROPERTY_TRANSPARENCY, val); + if (result != 0) { + qFatal("QBBScreen: 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(mAppWindow, SCREEN_PROPERTY_SWAP_INTERVAL, val); + if (result != 0) { + qFatal("QBBScreen: 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] = mCurrentGeometry.width(); + val[1] = mCurrentGeometry.height(); + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window source size, errno=%d", errno); + } + + errno = 0; + val[0] = 1; + val[1] = 0; + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_SOURCE_POSITION, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window source position, errno=%d", errno); + } + + // generate a random window group name + QUuid uuid; + mWindowGroupName = QUuid::createUuid().toString().toAscii(); + + // create window group so child windows can be parented by container window + errno = 0; + result = screen_create_window_group(mAppWindow, windowGroupName()); + if (result != 0) { + qFatal("QBBScreen: failed to create app window group, errno=%d", errno); + } + + errno = 0; + screen_buffer_t buffer; + result = screen_get_window_property_pv(mAppWindow, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&buffer); + if (result != 0) { + qFatal("QBBScreen: failed to query window buffer, errno=%d", errno); + } + + errno = 0; + int dirtyRect[] = {0, 0, 1, 1}; + result = screen_post_window(mAppWindow, buffer, 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBB: failed to post window buffer, errno=%d", errno); + } +} + +void QBBScreen::destroyRootWindow() +{ + if (!mAppWindow) + return; + + // cleanup top-level QNX window + screen_destroy_window(mAppWindow); + mAppWindow = 0; + mWindowGroupName = QByteArray(); +} + +QRect QBBScreen::availableGeometry() const +{ + // available geometry = total geometry - keyboard + int keyboardHeight = QBBVirtualKeyboard::instance().getHeight(); + return QRect(mCurrentGeometry.x(), mCurrentGeometry.y(), + mCurrentGeometry.width(), mCurrentGeometry.height() - keyboardHeight); +} + +void QBBScreen::setRotation(int rotation) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::setRotation, o=" << rotation; +#endif + + // check if rotation changed + if (mCurrentRotation != rotation) { + + // update rotation of app window + errno = 0; + int result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_ROTATION, &rotation); + if (result != 0) { + qFatal("QBBScreen: failed to set window rotation, errno=%d", errno); + } + + // swap dimensions if we've rotated 90 or 270 from initial orientation + if (orthogonal(mStartRotation, rotation)) { + mCurrentGeometry = QRect(0, 0, mStartGeometry.height(), mStartGeometry.width()); + mCurrentPhysicalSize = QSize(mStartPhysicalSize.height(), mStartPhysicalSize.width()); + } else { + mCurrentGeometry = QRect(0, 0, mStartGeometry.width(), mStartGeometry.height()); + mCurrentPhysicalSize = mStartPhysicalSize; + } + + // resize app window if we've rotated 90 or 270 from previous orientation + if (orthogonal(mCurrentRotation, rotation)) { + +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::setRotation - resize, s=" << mCurrentGeometry.size(); +#endif + errno = 0; + int val[] = {mCurrentGeometry.width(), mCurrentGeometry.height()}; + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window size, errno=%d", errno); + } + + errno = 0; + result = screen_set_window_property_iv(mAppWindow, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBScreen: failed to set window source size, errno=%d", errno); + } + + // NOTE: display will update when child windows relayout and repaint + + } else { + + // TODO: find one global place to flush display updates +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::setRotation - flush"; +#endif + // force immediate display update if no geometry changes required + errno = 0; + int result = screen_flush_context(mContext, 0); + if (result != 0) { + qFatal("QBBScreen: failed to flush context, errno=%d", errno); + } + } + + // save new rotation + mCurrentRotation = rotation; + } +} + +/*! + Check if the supplied angles are perpendicular to each other. +*/ +bool QBBScreen::orthogonal(int rotation1, int rotation2) +{ + return ((rotation1 - rotation2) % 180) != 0; +} + +void QBBScreen::addWindow(QBBWindow* window) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::addWindow=" << window; +#endif + + if (sChildren.contains(window)) + return; + + sChildren.push_back(window); + QBBScreen::updateHierarchy(); +} + +void QBBScreen::removeWindow(QBBWindow* window) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::removeWindow=" << window; +#endif + + sChildren.removeAll(window); + QBBScreen::updateHierarchy(); +} + +void QBBScreen::raiseWindow(QBBWindow* window) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::raise window=" << window; +#endif + + removeWindow(window); + sChildren.push_back(window); + QBBScreen::updateHierarchy(); +} + +void QBBScreen::lowerWindow(QBBWindow* window) +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::lower window=" << window; +#endif + + removeWindow(window); + sChildren.push_front(window); + QBBScreen::updateHierarchy(); +} + +void QBBScreen::updateHierarchy() +{ +#if defined(QBBSCREEN_DEBUG) + qDebug() << "QBBScreen::updateHierarchy"; +#endif + + QList<QBBWindow*>::iterator it; + QList<QPlatformScreen *>::iterator sit; + QMap<QPlatformScreen *, int> map; + int topZorder = 0; + + for (sit = sScreens.begin(); sit != sScreens.end(); sit++) + map[*sit] = 0; + + for (it = sChildren.begin(); it != sChildren.end(); it++) { + (*it)->updateZorder(topZorder); + map[static_cast<QBBScreen*>((*it)->screen())] = 1; + } + + // Check to see if any root windows need destruction + for (sit = sScreens.begin(); sit != sScreens.end(); sit++) + if (!static_cast<QBBScreen*>(*sit)->isPrimaryDisplay() && map[*sit] == 0) + static_cast<QBBScreen*>(*sit)->destroyRootWindow(); + + // After a hierarchy update, we need to force a flush on all screens. + // Right now, all screens share a context. + screen_flush_context( primaryDisplay()->mContext, 0 ); +} + +void QBBScreen::onWindowPost(QBBWindow* window) +{ + Q_UNUSED(window) + + // post app window (so navigator will show it) after first child window + // has posted; this only needs to happen once as the app window's content + // never changes + if (!mPosted) { + + errno = 0; + screen_buffer_t buffer; + int result = screen_get_window_property_pv(mAppWindow, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&buffer); + if (result != 0) { + qFatal("QBB: failed to query window buffer, errno=%d", errno); + } + + errno = 0; + int dirtyRect[] = {0, 0, 1, 1}; + result = screen_post_window(mAppWindow, buffer, 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBB: failed to post window buffer, errno=%d", errno); + } + + mPosted = true; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/blackberry/qbbscreen.h b/src/plugins/platforms/blackberry/qbbscreen.h new file mode 100644 index 0000000..a061811 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbscreen.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBSCREEN_H +#define QBBSCREEN_H + +#include <QtGui/QPlatformScreen> +#include <QByteArray> + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +class QBBWindow; + +class QBBScreen : public QPlatformScreen +{ +public: + static QList<QPlatformScreen *> screens() { return sScreens; } + static void createDisplays(screen_context_t context); + static void destroyDisplays(); + static QBBScreen* primaryDisplay() { return static_cast<QBBScreen*>(sScreens.at(0)); } + static int defaultDepth(); + + virtual QRect geometry() const { return mCurrentGeometry; } + 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 QSize physicalSize() const { return mCurrentPhysicalSize; } + + int rotation() const { return mCurrentRotation; } + void setRotation(int rotation); + + int nativeFormat() const { return (depth() == 32) ? SCREEN_FORMAT_RGBA8888 : SCREEN_FORMAT_RGB565; } + screen_display_t nativeDisplay() const { return mDisplay; } + screen_context_t nativeContext() const { return mContext; } + const char *windowGroupName() const { return mWindowGroupName.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); + void ensureDisplayCreated(); + +private: + screen_context_t mContext; + screen_display_t mDisplay; + screen_window_t mAppWindow; + QByteArray mWindowGroupName; + bool mPosted; + bool mUsingOpenGL; + bool mPrimaryDisplay; + + int mStartRotation; + int mCurrentRotation; + QSize mStartPhysicalSize; + QSize mCurrentPhysicalSize; + QRect mStartGeometry; + QRect mCurrentGeometry; + + static QList<QPlatformScreen *> sScreens; + static QList<QBBWindow*> sChildren; + + QBBScreen(screen_context_t context, screen_display_t display, bool primary); + virtual ~QBBScreen(); + + static bool orthogonal(int rotation1, int rotation2); + void createRootWindow(); + void destroyRootWindow(); + bool isPrimaryDisplay() { return mPrimaryDisplay; } +}; + +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 0000000..5e7d1fe --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QBBVIRTUALKEYBOARD_DEBUG + +#include "qbbvirtualkeyboard.h" + +#include <QDebug> +#include <QtGui/QApplication> +#include <QtGui/QPlatformScreen> +#include <QtGui/QPlatformWindow> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/iomsg.h> +#include <sys/pps.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +const char *QBBVirtualKeyboard::sPPSPath = "/pps/services/input/control?wait"; +const size_t QBBVirtualKeyboard::sBufferSize = 2048; + +static QBBVirtualKeyboard* s_instance = NULL; + +// Huge hack for keyboard shadow (see QNX PR 88400). Should be removed ASAP. +#define KEYBOARD_SHADOW_HEIGHT 8 + +QBBVirtualKeyboard::QBBVirtualKeyboard() : + mEncoder(NULL), + mDecoder(NULL), + mBuffer(NULL), + mHeight(0), + mFd(-1), + mKeyboardMode(Default), + mVisible(false), + mLanguageId(QString::fromLatin1("en")), + mCountryId(QString::fromLatin1("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 (mFd) { + // any reads will fail after we close the fd, which is basically what we want. + ::close(mFd); + } + + // Wait for the thread to die (should be immediate), then continue the cleanup. + wait(); + + if (mFd) { + mFd = -1; + } + + + if (mDecoder) + { + pps_decoder_cleanup(mDecoder); + delete mDecoder; + mDecoder = NULL; + } + + if (mEncoder) + { + pps_encoder_cleanup(mEncoder); + delete mEncoder; + mEncoder = NULL; + } + + delete [] mBuffer; + mBuffer = NULL; +} + +bool QBBVirtualKeyboard::connect() +{ + close(); + + mEncoder = new pps_encoder_t; + mDecoder = new pps_decoder_t; + + pps_encoder_initialize(mEncoder, false); + pps_decoder_initialize(mDecoder, NULL); + + errno = 0; + mFd = ::open(sPPSPath, O_RDWR); + if (mFd == -1) + { + qCritical("QBBVirtualKeyboard: Unable to open \"%s\" for keyboard: %s (%d).", + sPPSPath, strerror(errno), errno); + close(); + return false; + } + + mBuffer = new char[sBufferSize]; + if (!mBuffer) { + qCritical("QBBVirtualKeyboard: Unable to allocate buffer of %d bytes. Size is unavailable.", sBufferSize); + return false; + } + + if (!queryPPSInfo()) + return false; + + start(); + + return true; +} + +bool QBBVirtualKeyboard::queryPPSInfo() +{ + // Request info, requires id to regenerate res message. + pps_encoder_add_string(mEncoder, "msg", "info"); + pps_encoder_add_string(mEncoder, "id", "libWebView"); + + if (::write(mFd, pps_encoder_buffer(mEncoder), pps_encoder_length(mEncoder)) == -1) { + close(); + return false; + } + + pps_encoder_reset(mEncoder); + + return true; +} + +void QBBVirtualKeyboard::notifyClientActiveStateChange(bool active) +{ + if (!active) + hideKeyboard(); +} + +void QBBVirtualKeyboard::run() +{ + ppsDataReady(); +} + +void QBBVirtualKeyboard::ppsDataReady() +{ + forever { + ssize_t nread = read(mFd, mBuffer, sBufferSize - 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<size_t>(nread) > sBufferSize - 1) { + qCritical("QBBVirtualKeyboard: Keyboard buffer size too short; need %u.", nread + 1); + break; + } + + mBuffer[nread] = 0; + pps_decoder_parse_pps_str(mDecoder, mBuffer); + pps_decoder_push(mDecoder, NULL); +#ifdef QBBVIRTUALKEYBOARD_DEBUG + pps_decoder_dump_tree(mDecoder, stderr); +#endif + + const char* value; + if (pps_decoder_get_string(mDecoder, "error", &value) == PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS decoder error: %s", value ? value : "[null]"); + continue; + } + + if (pps_decoder_get_string(mDecoder, "msg", &value) == PPS_DECODER_OK) { + if (strcmp(value, "show") == 0) { + mVisible = true; + handleKeyboardStateChangeMessage(true); + } else if (strcmp(value, "hide") == 0) { + mVisible = false; + handleKeyboardStateChangeMessage(false); + } 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(mDecoder, "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 (mDecoder) + pps_decoder_cleanup(mDecoder); +} + +void QBBVirtualKeyboard::handleKeyboardInfoMessage() +{ + int newHeight = 0; + const char* value; + + if (pps_decoder_push(mDecoder, "dat") != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS dat object not found"); + return; + } + if (pps_decoder_get_int(mDecoder, "size", &newHeight) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS size field not found"); + return; + } + if (pps_decoder_push(mDecoder, "locale") != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS locale object not found"); + return; + } + if (pps_decoder_get_string(mDecoder, "languageId", &value) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS languageId field not found"); + mLanguageId = QString::fromLatin1(value); + return; + } + if (pps_decoder_get_string(mDecoder, "countryId", &value) != PPS_DECODER_OK) { + qCritical("QBBVirtualKeyboard: Keyboard PPS size countryId not found"); + mCountryId = QString::fromLatin1(value); + return; + } + + + // 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 != mHeight) { + mHeight = newHeight; + handleKeyboardStateChangeMessage(true); + } + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: handleKeyboardInfoMessage size=" << mHeight << "languageId=" << mLanguageId << " countryId=" << mCountryId; +#endif +} + +void QBBVirtualKeyboard::handleKeyboardStateChangeMessage(bool visible) +{ + +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: handleKeyboardStateChangeMessage " << visible; +#endif + + // TODO: What screen index should be used? I assume 0 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. + QWindowSystemInterface::handleScreenAvailableGeometryChange(0); +} + +bool QBBVirtualKeyboard::showKeyboard() +{ +#ifdef QBBVIRTUALKEYBOARD_DEBUG + qDebug() << "QBB: showKeyboard()"; +#endif + + // Try to connect. + if (mFd == -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(mEncoder); + + // Send the show message. + pps_encoder_add_string(mEncoder, "msg", "show"); + + if (::write(mFd, pps_encoder_buffer(mEncoder), pps_encoder_length(mEncoder)) == -1) { + close(); + return false; + } + + pps_encoder_reset(mEncoder); + + // 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 (mFd == -1 && !connect()) + return false; + + pps_encoder_add_string(mEncoder, "msg", "hide"); + + if (::write(mFd, pps_encoder_buffer(mEncoder), pps_encoder_length(mEncoder)) == -1) { + close(); + + //Try again. + if (connect()) { + if (::write(mFd, pps_encoder_buffer(mEncoder), pps_encoder_length(mEncoder)) == -1) { + close(); + return false; + } + } + else + return false; + } + + pps_encoder_reset(mEncoder); + + // Return true if no error occurs. Sizing response will be triggered when confirmation of + // the change arrives. + return true; +} + +void QBBVirtualKeyboard::setKeyboardMode(KeyboardMode mode) +{ + mKeyboardMode = mode; +} + +void QBBVirtualKeyboard::applyKeyboardModeOptions() +{ + // Try to connect. + if (mFd == -1 && !connect()) + return; + + // Send the options message. + pps_encoder_add_string(mEncoder, "msg", "options"); + + pps_encoder_start_object(mEncoder, "dat"); + switch (mKeyboardMode) { + 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(mEncoder); + + if (::write(mFd, pps_encoder_buffer(mEncoder), pps_encoder_length(mEncoder)) == -1) { + close(); + } + + pps_encoder_reset(mEncoder); +} + +void QBBVirtualKeyboard::addDefaultModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "default"); +} + +void QBBVirtualKeyboard::addUrlModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "url"); +} + +void QBBVirtualKeyboard::addEmailModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "email"); +} + +void QBBVirtualKeyboard::addWebModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "web"); +} + +void QBBVirtualKeyboard::addNumPuncModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "numPunc"); +} + +void QBBVirtualKeyboard::addPhoneModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "phone"); +} + +void QBBVirtualKeyboard::addPinModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "pin"); +} + +void QBBVirtualKeyboard::addSymbolModeOptions() +{ + pps_encoder_add_string(mEncoder, "enter", "enter.default"); + pps_encoder_add_string(mEncoder, "type", "symbol"); +} diff --git a/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h new file mode 100644 index 0000000..bedcbe0 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbvirtualkeyboard.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIRTUALKEYBOARD_H_ +#define VIRTUALKEYBOARD_H_ + +#include <QtGui/QPlatformScreen> +#include <QtGui/QWindowSystemInterface> +#include <QThread> + +#include <stddef.h> +#include <vector> +#include <string> +#include <sys/pps.h> + +QT_BEGIN_NAMESPACE + +/* Shamelessly copied from the browser - this should be rewritten once we have a proper PPS wrapper class */ +class QBBVirtualKeyboard : QThread +{ +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 getHeight() { return mVisible ? mHeight : 0; } + void setKeyboardMode(KeyboardMode); + void notifyClientActiveStateChange(bool); + bool isVisible() const { return mVisible; } + QString languageId() const { return mLanguageId; } + QString countryId() const { return mCountryId; } + +private: + QBBVirtualKeyboard(); + virtual ~QBBVirtualKeyboard(); + + pps_encoder_t *mEncoder; + pps_decoder_t *mDecoder; + char *mBuffer; + int mHeight; + int mFd; + KeyboardMode mKeyboardMode; + bool mVisible; + QString mLanguageId; + QString mCountryId; + + // Path to keyboardManager in PPS. + static const char *sPPSPath; + static const size_t sBufferSize; + + // Will be called internally if needed. + bool connect(); + void close(); + void ppsDataReady(); + bool queryPPSInfo(); + void handleKeyboardStateChangeMessage(bool visible); + void handleKeyboardInfoMessage(); + + void applyKeyboardModeOptions(); + void addDefaultModeOptions(); + void addUrlModeOptions(); + void addEmailModeOptions(); + void addWebModeOptions(); + void addNumPuncModeOptions(); + void addSymbolModeOptions(); + void addPhoneModeOptions(); + void addPinModeOptions(); + + // QThread overrides + virtual void run(); + +}; + +#endif /* VIRTUALKEYBOARD_H_ */ diff --git a/src/plugins/platforms/blackberry/qbbwindow.cpp b/src/plugins/platforms/blackberry/qbbwindow.cpp new file mode 100644 index 0000000..ffdb118 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbwindow.cpp @@ -0,0 +1,669 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QBBWINDOW_DEBUG + +#include "qbbwindow.h" +#include "qbbglcontext.h" +#include "qbbscreen.h" + +#include <QtGui/QWidget> +#include <QDebug> +#include <QtGui/QPlatformWindowFormat> +#include <QtGui/QWindowSystemInterface> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QBBWindow::QBBWindow(QWidget *window, screen_context_t context) + : QPlatformWindow(window), + mContext(context), + mCurrentBufferIndex(-1), + mPreviousBufferIndex(-1), + mPlatformGlContext(NULL), + mScreen(NULL), + mParent(NULL), + mVisible(true) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::QBBWindow - w=" << window << ", s=" << window->size(); +#endif + int result; + + // create child QNX window + errno = 0; + result = screen_create_window_type(&mWindow, mContext, 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; + QPlatformWindowFormat format = widget()->platformWindowFormat(); + switch (format.windowApi()) { + case QPlatformWindowFormat::Raster: + val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE; + break; + case QPlatformWindowFormat::OpenGL: + val = SCREEN_USAGE_OPENGL_ES2; + break; + default: + qFatal("QBBWindow: unsupported window API"); + break; + } + + errno = 0; + result = screen_set_window_property_iv(mWindow, 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(mWindow, 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(mWindow, 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(mWindow, SCREEN_PROPERTY_SWAP_INTERVAL, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window swap interval, errno=%d", errno); + } + + // Set the screen 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); +} + +QBBWindow::~QBBWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::~QBBWindow - w=" << widget(); +#endif + + // 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 (mChildren.size() > 0) + qFatal("QBBWindow: window destroyed before children!"); + + // cleanup OpenGL/OpenVG context if it exists + if (mPlatformGlContext != NULL) { + delete mPlatformGlContext; + } + + // cleanup QNX window and its buffers + screen_destroy_window(mWindow); +} + +void QBBWindow::setGeometry(const QRect &rect) +{ + int val[2]; + +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::setGeometry - w=" << widget() << ", (" << rect.x() << "," << rect.y() << "," << rect.width() << "," << rect.height() << ")"; +#endif + + QRect oldGeometry = geometry(); + + // call parent method + QPlatformWindow::setGeometry(rect); + + // set window geometry equal to widget geometry + errno = 0; + val[0] = rect.x(); + val[1] = rect.y(); + int result = screen_set_window_property_iv(mWindow, 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(mWindow, 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(mWindow, 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<QBBWindow*>::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); it++) { + (*it)->offset(offset); + } + } +} + +void QBBWindow::offset(const QPoint &offset) +{ + // 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(mWindow, SCREEN_PROPERTY_POSITION, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window position, errno=%d", errno); + } + + QList<QBBWindow*>::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); it++) { + (*it)->offset(offset); + } +} + +void QBBWindow::setVisible(bool visible) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::setVisible - w=" << widget() << ", v=" << visible; +#endif + + mVisible = visible; + + QBBWindow* root = this; + while (root->mParent) + root = root->mParent; + + root->updateVisibility(root->mVisible); + + widget()->activateWindow(); +} + +void QBBWindow::updateVisibility(bool parentVisible) +{ + // set window visibility + errno = 0; + int val = (mVisible && parentVisible) ? 1 : 0; + int result = screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_VISIBLE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window visibility, errno=%d", errno); + } + + QList<QBBWindow*>::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); it++) { + (*it)->updateVisibility(mVisible && parentVisible); + } +} + +void QBBWindow::setOpacity(qreal level) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::setOpacity - w=" << widget() << ", o=" << level; +#endif + + // set window global alpha + errno = 0; + int val = (int)(level * 255); + int result = screen_set_window_property_iv(mWindow, 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() << "QBBWindow::setBufferSize - w=" << widget() << ", s=" << size; +#endif + + // set window buffer size + errno = 0; + int val[2] = { size.width(), size.height() }; + int result = screen_set_window_property_iv(mWindow, 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()) { +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::setBufferSize - create buffers"; +#endif + + // get pixel format from EGL config if using OpenGL; + // otherwise inherit pixel format of window's screen + if (mPlatformGlContext != NULL) { + val[0] = platformWindowFormatToNativeFormat(mPlatformGlContext->platformWindowFormat()); + } else { + val[0] = mScreen->nativeFormat(); + } + + errno = 0; + result = screen_set_window_property_iv(mWindow, 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(mWindow, MAX_BUFFER_COUNT); + if (result != 0) { + qFatal("QBBWindow: failed to create window buffers, errno=%d", errno); + } + } + + // cache new buffer size + mBufferSize = size; + + // buffers were destroyed; reacquire them + mCurrentBufferIndex = -1; + mPreviousDirty = QRegion(); + mScrolled = QRegion(); +} + +QBBBuffer &QBBWindow::renderBuffer() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::renderBuffer - w=" << widget(); +#endif + + // check if render buffer is invalid + if (mCurrentBufferIndex == -1) { + + // get all buffers available for rendering + errno = 0; + screen_buffer_t buffers[MAX_BUFFER_COUNT]; + int result = screen_get_window_property_pv(mWindow, 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++) { + mBuffers[i] = QBBBuffer(buffers[i]); + } + + // use the first available render buffer + mCurrentBufferIndex = 0; + mPreviousBufferIndex = -1; + } + + return mBuffers[mCurrentBufferIndex]; +} + +void QBBWindow::scroll(const QRegion ®ion, int dx, int dy, bool flush) +{ + copyBack(region, dx, dy, flush); + mScrolled += region; +} + +void QBBWindow::post(const QRegion &dirty) +{ + // check if render buffer exists and something was rendered + if (mCurrentBufferIndex != -1 && !dirty.isEmpty()) { + +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::post - w=" << widget(); +#endif + QBBBuffer ¤tBuffer = mBuffers[mCurrentBufferIndex]; + + // copy unmodified region from old render buffer to new render buffer; + // required to allow partial updates + QRegion preserve = mPreviousDirty - dirty - mScrolled; + copyBack(preserve, 0, 0); + + // calculate region that changed + QRegion modified = preserve + dirty + mScrolled; + 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(mWindow, currentBuffer.nativeBuffer(), 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBBWindow: failed to post window buffer, errno=%d", errno); + } + + // advance to next nender buffer + mPreviousBufferIndex = mCurrentBufferIndex++; + if (mCurrentBufferIndex >= MAX_BUFFER_COUNT) { + mCurrentBufferIndex = 0; + } + + // save modified region and clear scrolled region + mPreviousDirty = dirty; + mScrolled = QRegion(); + + // notify screen that window posted + if (mScreen != NULL) { + mScreen->onWindowPost(this); + } + } +} + +QPlatformGLContext *QBBWindow::glContext() const +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::glContext - w=" << widget(); +#endif + // create opengl context on first access if rendering API is correct + QPlatformWindowFormat format = widget()->platformWindowFormat(); + if (mPlatformGlContext == NULL && format.windowApi() == QPlatformWindowFormat::OpenGL) { + mPlatformGlContext = new QBBGLContext( const_cast<QBBWindow*>(this) ); + } + return mPlatformGlContext; +} + +void QBBWindow::setScreen(QBBScreen *platformScreen) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::setScreen - w=" << widget(); +#endif + + if (mScreen == platformScreen) + return; + + mScreen = platformScreen; + + // The display may not have a root (desktop) window yet so we must ensure that one has been + // created before we can join its group or get its native display property. + mScreen->ensureDisplayCreated(); + + // move window to proper display + errno = 0; + screen_display_t display = platformScreen->nativeDisplay(); + int result = screen_set_window_property_pv(mWindow, 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(mWindow, platformScreen->windowGroupName()); + if (result != 0) { + qFatal("QBBWindow: failed to join window group, errno=%d", errno); + } + + QList<QBBWindow*>::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); it++) { + // Only subwindows and tooltips need necessarily be moved to another display with + // the window. + if ((widget()->windowType() & Qt::WindowType_Mask) == Qt::SubWindow || + (widget()->windowType() & Qt::WindowType_Mask) == Qt::ToolTip) + (*it)->setScreen(platformScreen); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::removeFromParent() +{ + // Remove from old Hierarchy position + if (mParent) { + if (mParent->mChildren.removeAll(this)) + mParent = 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() << "QBBWindow::setParent - w=" << widget() << " p=" << window; +#endif + + // Cast away the const, we need to modify the Hierarchy. + QBBWindow* newParent = 0; + + if (window) + newParent = static_cast<QBBWindow*>((QPlatformWindow *)window); + + if (newParent == mParent) + return; + + removeFromParent(); + mParent = newParent; + + // Add to new Hierarchy position. + if (mParent) { + if (mParent->mScreen != mScreen) + setScreen(mParent->mScreen); + + mParent->mChildren.push_back(this); + } else { + QBBScreen::addWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::raise() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::raise - w=" << widget(); +#endif + + QBBWindow* oldParent = mParent; + if (oldParent) { + removeFromParent(); + oldParent->mChildren.push_back(this); + } else { + QBBScreen::raiseWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::lower() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::lower - w=" << widget(); +#endif + + QBBWindow* oldParent = mParent; + if (oldParent) { + removeFromParent(); + oldParent->mChildren.push_front(this); + } else { + QBBScreen::lowerWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::requestActivateWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::requestActivateWindow - w=" << widget(); +#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() << "QBBWindow::gainedFocus - w=" << widget(); +#endif + + // Got focus + QWindowSystemInterface::handleWindowActivated(widget()); +} + +void QBBWindow::updateZorder(int &topZorder) +{ + errno = 0; + int result = screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_ZORDER, &topZorder); + topZorder++; + + if (result != 0) + qFatal("QBBWindow: failed to set window z-order=%d, errno=%d, mWindow=0x%08x", topZorder, errno, mWindow); + + QList<QBBWindow*>::const_iterator it; + + for (it = mChildren.begin(); it != mChildren.end(); it++) + (*it)->updateZorder(topZorder); +} + +void QBBWindow::copyBack(const QRegion ®ion, int dx, int dy, bool flush) +{ + int result; + + // abort if previous buffer is invalid + if (mPreviousBufferIndex == -1) { + return; + } + + // abort if nothing to copy + if (region.isEmpty()) { + return; + } + + QBBBuffer ¤tBuffer = mBuffers[mCurrentBufferIndex]; + QBBBuffer &previousBuffer = mBuffers[mPreviousBufferIndex]; + + // break down region into non-overlapping rectangles + QVector<QRect> 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(mContext, 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(mContext, 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 QPlatformWindowFormat &format) +{ + // 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 0000000..5168dfa --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbwindow.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** +** Contact: Research In Motion <blackberry-qt@qnx.com> +** Contact: Klarälvdalens Datakonsult AB <info@kdab.com> +** +** This file is part of the QtCore module 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBBWINDOW_H +#define QBBWINDOW_H + +#include "qbbbuffer.h" + +#include <QImage> +#include <QtGui/QPlatformWindow> + +#include <screen/screen.h> +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +// all surfaces double buffered +#define MAX_BUFFER_COUNT 2 + +class QPlatformWindowFormat; +class QBBGLContext; +class QBBScreen; + +class QBBWindow : public QPlatformWindow +{ +friend class QBBScreen; +public: + QBBWindow(QWidget *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)mWindow; } + + void setBufferSize(const QSize &size); + QSize bufferSize() const { return mBufferSize; } + bool hasBuffers() const { return !mBufferSize.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 QPlatformGLContext *glContext() const; + + virtual void setParent(const QPlatformWindow *window); + virtual void raise(); + virtual void lower(); + virtual void requestActivateWindow(); + + void gainedFocus(); + + QBBScreen* screen() const { return mScreen; } + const QList<QBBWindow*>& children() const { return mChildren; } + +private: + screen_context_t mContext; + screen_window_t mWindow; + QSize mBufferSize; + QBBBuffer mBuffers[MAX_BUFFER_COUNT]; + int mCurrentBufferIndex; + int mPreviousBufferIndex; + QRegion mPreviousDirty; + QRegion mScrolled; + + mutable QBBGLContext* mPlatformGlContext; + QBBScreen* mScreen; + QList<QBBWindow*> mChildren; + QBBWindow *mParent; + bool mVisible; + + 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 QPlatformWindowFormat &format); +}; + +QT_END_NAMESPACE + +#endif // QBBWINDOW_H |