diff options
Diffstat (limited to 'src/plugins/platforms/qnx')
31 files changed, 1609 insertions, 1472 deletions
diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index bc7219de5c..c605126091 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -45,7 +45,8 @@ SOURCES = main.cpp \ qqnxabstractvirtualkeyboard.cpp \ qqnxservices.cpp \ qqnxcursor.cpp \ - qqnxrasterwindow.cpp + qqnxrasterwindow.cpp \ + qqnxglobal.cpp HEADERS = main.h \ qqnxbuffer.h \ @@ -62,7 +63,9 @@ HEADERS = main.h \ qqnxabstractcover.h \ qqnxservices.h \ qqnxcursor.h \ - qqnxrasterwindow.h + qqnxrasterwindow.h \ + qqnxscreeneventfilter.h \ + qqnxglobal.h CONFIG(qqnx_screeneventthread) { DEFINES += QQNX_SCREENEVENTTHREAD diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp index a42f73415e..800cb96bdf 100644 --- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp +++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -49,6 +49,7 @@ QQnxAbstractVirtualKeyboard::QQnxAbstractVirtualKeyboard(QObject *parent) , m_visible(false) , m_locale(QLocale::system()) , m_keyboardMode(Default) + , m_enterKeyType(DefaultReturn) { } @@ -59,26 +60,35 @@ void QQnxAbstractVirtualKeyboard::setKeyboardMode(KeyboardMode mode) m_keyboardMode = mode; - applyKeyboardMode(mode); + if (m_visible) + applyKeyboardOptions(); } -void QQnxAbstractVirtualKeyboard::setInputHintsFromObject(QObject *focusObject) +void QQnxAbstractVirtualKeyboard::setEnterKeyType(EnterKeyType type) { - if (focusObject) { - const Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>( - focusObject->property("inputMethodHints").toInt()); - if (hints & Qt::ImhEmailCharactersOnly) { - setKeyboardMode(QQnxAbstractVirtualKeyboard::Email); - } else if (hints & Qt::ImhDialableCharactersOnly) { - setKeyboardMode(QQnxAbstractVirtualKeyboard::Phone); - } else if (hints & Qt::ImhUrlCharactersOnly) { - setKeyboardMode(QQnxAbstractVirtualKeyboard::Web); - } else if (hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhDigitsOnly || - hints & Qt::ImhDate || hints & Qt::ImhTime) { - setKeyboardMode(QQnxAbstractVirtualKeyboard::NumPunc); - } else { - setKeyboardMode(QQnxAbstractVirtualKeyboard::Default); - } + if (type == m_enterKeyType) + return; + + m_enterKeyType = type; + + if (m_visible) + applyKeyboardOptions(); +} + +void QQnxAbstractVirtualKeyboard::setInputHints(int inputHints) +{ + if (inputHints & Qt::ImhEmailCharactersOnly) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::Email); + } else if (inputHints & Qt::ImhDialableCharactersOnly) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::Phone); + } else if (inputHints & Qt::ImhUrlCharactersOnly) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::Url); + } else if (inputHints & Qt::ImhFormattedNumbersOnly || inputHints & Qt::ImhDigitsOnly) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::Number); + } else if (inputHints & Qt::ImhDate || inputHints & Qt::ImhTime) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::NumPunc); // Use NumPunc so that : is available. + } else if (inputHints & Qt::ImhHiddenText) { + setKeyboardMode(QQnxAbstractVirtualKeyboard::Password); } else { setKeyboardMode(QQnxAbstractVirtualKeyboard::Default); } diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h index 9b911e1dec..bff8c56835 100644 --- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h +++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -51,18 +51,20 @@ class QQnxAbstractVirtualKeyboard : public QObject { Q_OBJECT public: - // NOTE: Not all the following keyboard modes are currently used. + // Keyboard Types currently supported. // 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 + // Number - Number pad // 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. + // Phone - Phone enhanced keyboard + // Pin - Keyboard for entering Pins (Hex values). + // Password - Keyboard with lots of extra characters for password input. + // Alphanumeric - Similar to password without any of the security implications. // - // 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 }; + enum KeyboardMode { Default, Url, Email, Web, NumPunc, Number, Symbol, Phone, Pin, Password, Alphanumeric }; + enum EnterKeyType { DefaultReturn, Connect, Done, Go, Join, Next, Search, Send, Submit }; explicit QQnxAbstractVirtualKeyboard(QObject *parent = 0); @@ -74,8 +76,11 @@ public: QLocale locale() const { return m_locale; } void setKeyboardMode(KeyboardMode mode); - void setInputHintsFromObject(QObject *focusObject); + void setEnterKeyType(EnterKeyType type); + + void setInputHints(int inputHints); KeyboardMode keyboardMode() const { return m_keyboardMode; } + EnterKeyType enterKeyType() const { return m_enterKeyType; } Q_SIGNALS: void heightChanged(int height); @@ -83,7 +88,7 @@ Q_SIGNALS: void localeChanged(const QLocale &locale); protected: - virtual void applyKeyboardMode(KeyboardMode mode) = 0; + virtual void applyKeyboardOptions() = 0; void setHeight(int height); void setVisible(bool visible); @@ -94,6 +99,7 @@ private: bool m_visible; QLocale m_locale; KeyboardMode m_keyboardMode; + EnterKeyType m_enterKeyType; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxbuffer.cpp b/src/plugins/platforms/qnx/qqnxbuffer.cpp index abb8a07026..e9afd5232b 100644 --- a/src/plugins/platforms/qnx/qqnxbuffer.cpp +++ b/src/plugins/platforms/qnx/qqnxbuffer.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxbuffer.h" #include <QtCore/QDebug> @@ -66,34 +68,30 @@ QQnxBuffer::QQnxBuffer(screen_buffer_t buffer) qBufferDebug() << Q_FUNC_INFO << "normal"; // 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("QQNX: failed to query buffer size, errno=%d", errno); + Q_SCREEN_CRITICALERROR(screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_BUFFER_SIZE, size), + "Failed to query buffer size"); // Get stride of buffer - errno = 0; int stride; - result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride); - if (result != 0) - qFatal("QQNX: failed to query buffer stride, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride), + "Failed to query buffer stride"); // Get access to buffer's data errno = 0; uchar *dataPtr = 0; - result = screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, (void **)&dataPtr); - if (result != 0) - qFatal("QQNX: failed to query buffer pointer, errno=%d", errno); + Q_SCREEN_CRITICALERROR( + screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, (void **)&dataPtr), + "Failed to query buffer pointer"); + if (dataPtr == 0) qFatal("QQNX: 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("QQNX: failed to query buffer format, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_FORMAT, &screenFormat), + "Failed to query buffer format"); // Convert screen format to QImage format QImage::Format imageFormat = QImage::Format_Invalid; diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp index 984de67d7d..45a7bab871 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp @@ -86,8 +86,9 @@ void QQnxEglWindow::createEGLSurface() // the window's buffers before we create the EGL surface const QSize surfaceSize = requestedBufferSize(); if (!surfaceSize.isValid()) { - qFatal("QQNX: Trying to create 0 size EGL surface. " + qWarning("QQNX: Trying to create 0 size EGL surface. " "Please set a valid window size before calling QOpenGLContext::makeCurrent()"); + return; } setBufferSize(surfaceSize); @@ -104,8 +105,8 @@ void QQnxEglWindow::createEGLSurface() , platformOpenGLContext()->getEglConfig(), (EGLNativeWindowType) nativeHandle(), eglSurfaceAttrs); if (m_eglSurface == EGL_NO_SURFACE) { - QQnxGLContext::checkEGLError("eglCreateWindowSurface"); - qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError()); + const EGLenum error = QQnxGLContext::checkEGLError("eglCreateWindowSurface"); + qWarning("QQNX: failed to create EGL surface, err=%d", error); } } @@ -141,6 +142,8 @@ void QQnxEglWindow::swapEGLBuffers() EGLSurface QQnxEglWindow::getSurface() { if (m_newSurfaceRequested.testAndSetOrdered(true, false)) { + const QMutexLocker locker(&m_mutex); //Set geomety must not reset the requestedBufferSize till + //the surface is created if (m_eglSurface != EGL_NO_SURFACE) { platformOpenGLContext()->doneCurrent(); destroyEGLSurface(); @@ -172,17 +175,9 @@ void QQnxEglWindow::setGeometry(const QRect &rect) QSize QQnxEglWindow::requestedBufferSize() const { - const QMutexLocker locker(&m_mutex); return m_requestedBufferSize; } -void QQnxEglWindow::adjustBufferSize() -{ - const QSize windowSize = window()->size(); - if (windowSize != bufferSize()) - setBufferSize(windowSize); -} - void QQnxEglWindow::setPlatformOpenGLContext(QQnxGLContext *platformOpenGLContext) { // This function does not take ownership of the platform gl context. @@ -220,7 +215,6 @@ int QQnxEglWindow::pixelFormat() const void QQnxEglWindow::resetBuffers() { - const QMutexLocker locker(&m_mutex); m_requestedBufferSize = QSize(); } diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.h b/src/plugins/platforms/qnx/qqnxeglwindow.h index fc53afcd7a..a6a223c58e 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.h +++ b/src/plugins/platforms/qnx/qqnxeglwindow.h @@ -68,8 +68,6 @@ public: // Called by QQnxGLContext::createSurface() QSize requestedBufferSize() const; - void adjustBufferSize(); - protected: int pixelFormat() const; void resetBuffers(); diff --git a/src/plugins/platforms/qnx/qqnxglcontext.cpp b/src/plugins/platforms/qnx/qqnxglcontext.cpp index 34e8150928..3a365be408 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.cpp +++ b/src/plugins/platforms/qnx/qqnxglcontext.cpp @@ -132,7 +132,7 @@ QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext) } } - m_eglContext = eglCreateContext(ms_eglDisplay, m_eglConfig, shareContext, contextAttrs()); + m_eglContext = eglCreateContext(ms_eglDisplay, m_eglConfig, shareContext, contextAttrs(format)); if (m_eglContext == EGL_NO_CONTEXT) { checkEGLError("eglCreateContext"); qFatal("QQnxGLContext: failed to create EGL context, err=%d", eglGetError()); @@ -227,7 +227,8 @@ bool QQnxGLContext::makeCurrent(QPlatformSurface *surface) eglResult = eglMakeCurrent(ms_eglDisplay, m_currentEglSurface, m_currentEglSurface, m_eglContext); if (eglResult != EGL_TRUE) { checkEGLError("eglMakeCurrent"); - qFatal("QQNX: failed to set current EGL context, err=%d", eglGetError()); + qWarning("QQNX: failed to set current EGL context, err=%d", eglGetError()); + return false; } return (eglResult == EGL_TRUE); } @@ -274,13 +275,13 @@ EGLDisplay QQnxGLContext::getEglDisplay() { return ms_eglDisplay; } -EGLint *QQnxGLContext::contextAttrs() +EGLint *QQnxGLContext::contextAttrs(const QSurfaceFormat &format) { qGLContextDebug() << Q_FUNC_INFO; // Choose EGL settings based on OpenGL version #if defined(QT_OPENGL_ES_2) - static EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + static EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, format.version().first, EGL_NONE }; return attrs; #else return 0; diff --git a/src/plugins/platforms/qnx/qqnxglcontext.h b/src/plugins/platforms/qnx/qqnxglcontext.h index 2b12657da9..af89586bd5 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.h +++ b/src/plugins/platforms/qnx/qqnxglcontext.h @@ -88,7 +88,7 @@ private: EGLContext m_eglContext; EGLSurface m_currentEglSurface; - static EGLint *contextAttrs(); + static EGLint *contextAttrs(const QSurfaceFormat &format); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxglobal.cpp b/src/plugins/platforms/qnx/qqnxglobal.cpp new file mode 100644 index 0000000000..cef37af84e --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxglobal.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2014 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <errno.h> + +#include <QDebug> +#include "qqnxintegration.h" + +QT_BEGIN_NAMESPACE + +void qScreenCheckError(int rc, const char *funcInfo, const char *message, bool critical) +{ + if (!rc && (QQnxIntegration::options() & QQnxIntegration::AlwaysFlushScreenContext) + && QQnxIntegration::screenContext() != 0) { + rc = screen_flush_context(QQnxIntegration::screenContext(), 0); + } + + if (rc) { + if (critical) + qCritical("%s - Screen: %s - Error: %s (%i)", funcInfo, message, strerror(errno), errno); + else + qWarning("%s - Screen: %s - Error: %s (%i)", funcInfo, message, strerror(errno), errno); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxglobal.h b/src/plugins/platforms/qnx/qqnxglobal.h new file mode 100644 index 0000000000..8cfbfb084a --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxglobal.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2014 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXGLOBAL_H +#define QQNXGLOBAL_H + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +void qScreenCheckError(int rc, const char *funcInfo, const char *message, bool critical); + +#define Q_SCREEN_CHECKERROR(x, message) \ +qScreenCheckError(x, Q_FUNC_INFO, message, false) + +#define Q_SCREEN_CRITICALERROR(x, message) \ +qScreenCheckError(x, Q_FUNC_INFO, message, true) + +QT_END_NAMESPACE + +#endif // QQNXGLOBAL_H diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp index 580553f6e2..619883e843 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp +++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -40,10 +40,10 @@ ****************************************************************************/ #include "qqnxinputcontext_imf.h" -#include "qqnxeventthread.h" #include "qqnxabstractvirtualkeyboard.h" #include "qqnxintegration.h" #include "qqnxscreen.h" +#include "qqnxscreeneventhandler.h" #include <QtGui/QGuiApplication> #include <QtGui/QInputMethodEvent> @@ -54,6 +54,8 @@ #include <QtCore/QVariant> #include <QtCore/QVariantHash> #include <QtCore/QWaitCondition> +#include <QtCore/QQueue> +#include <QtCore/QGlobalStatic> #include <dlfcn.h> #include "imf/imf_client.h" @@ -62,9 +64,9 @@ #include <sys/keycodes.h> #if defined(QQNXINPUTCONTEXT_IMF_EVENT_DEBUG) -#define qInputContextIMFEventDebug qDebug +#define qInputContextIMFRequestDebug qDebug #else -#define qInputContextIMFEventDebug QT_NO_QDEBUG_MACRO +#define qInputContextIMFRequestDebug QT_NO_QDEBUG_MACRO #endif #if defined(QQNXINPUTCONTEXT_DEBUG) @@ -73,492 +75,426 @@ #define qInputContextDebug QT_NO_QDEBUG_MACRO #endif -/** TODO: - Support inputMethodHints to restrict input (needs additional features in IMF). -*/ +static QQnxInputContext *sInputContextInstance; +static QColor sSelectedColor(0,0xb8,0,85); -#define STRX(x) #x -#define STR(x) STRX(x) - -// Someone tell me why input_control methods are in this namespace, but the rest is not. -using namespace InputMethodSystem; - -#define qs(x) QString::fromLatin1(x) -#define iarg(name) event->mArgs[qs(#name)] = QVariant::fromValue(name) -#define parg(name) event->mArgs[qs(#name)] = QVariant::fromValue((void*)name) -namespace -{ - -spannable_string_t *toSpannableString(const QString &text); +static const input_session_t *sSpellCheckSession = 0; static const input_session_t *sInputSession = 0; -bool isSessionOkay(input_session_t *ic) +static 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 + ImfIsTextSelected, + ImfIsAllTextSelected, }; -// We use this class as a round about way to support a posting synchronous event into -// Qt's main thread from the IMF thread. -class ImfEventResult -{ -public: - ImfEventResult() - { - m_mutex.lock(); - } - - ~ImfEventResult() - { - m_mutex.unlock(); - } - - void wait() - { - m_wait.wait(&m_mutex); - } - - void signal() - { - m_wait.wakeAll(); - } - - void setResult(const QVariant& result) - { - m_mutex.lock(); - m_retVal = result; - signal(); - m_mutex.unlock(); - } - - QVariant result() - { - return m_retVal; - } - -private: - QVariant m_retVal; - QMutex m_mutex; - QWaitCondition m_wait; +struct SpellCheckInfo { + SpellCheckInfo(void *_context, void (*_spellCheckDone)(void *, const QString &, const QList<int> &)) + : context(_context), spellCheckDone(_spellCheckDone) {} + void *context; + void (*spellCheckDone)(void *, const QString &, const QList<int> &); }; +Q_GLOBAL_STATIC(QQueue<SpellCheckInfo>, sSpellCheckQueue) -class ImfEvent : public QEvent +// IMF requests all arrive on IMF's own thread and have to be posted to the main thread to be processed. +class QQnxImfRequest { - public: - ImfEvent(input_session_t *session, ImfEventType type, ImfEventResult *result) : - QEvent((QEvent::Type)sUserEventType), - m_session(session), - m_imfType(type), - m_result(result) - { - } - ~ImfEvent() { } - - input_session_t *m_session; - ImfEventType m_imfType; - QVariantHash m_args; - ImfEventResult *m_result; - - static int sUserEventType; +public: + QQnxImfRequest(input_session_t *_session, ImfEventType _type) + : session(_session), type(_type) + { } + ~QQnxImfRequest() { } + + input_session_t *session; + ImfEventType type; + union { + struct { + int32_t n; + int32_t flags; + bool before; + spannable_string_t *result; + } gtac; // ic_get_text_before_cursor/ic_get_text_after_cursor + struct { + int32_t result; + } gcp; // ic_get_cursor_position + struct { + int32_t start; + int32_t end; + int32_t result; + } scr; // ic_set_composing_region + struct { + spannable_string_t* text; + int32_t new_cursor_position; + int32_t result; + } sct; // ic_set_composing_text + struct { + spannable_string_t* text; + int32_t new_cursor_position; + int32_t result; + } ct; // ic_commit_text + struct { + int32_t result; + } fct; // ic_finish_composing_text + struct { + int32_t left_length; + int32_t right_length; + int32_t result; + } dst; // ic_delete_surrounding_text + struct { + event_t *event; + int32_t result; + } sae; // ic_send_async_event/ic_send_event + struct { + int32_t *pIsSelected; + int32_t result; + } its; // ic_is_text_selected/ic_is_all_text_selected + }; }; -int ImfEvent::sUserEventType = QEvent::registerEventType(); -static int32_t imfBeginBatchEdit(input_session_t *ic) +// Invoke an IMF initiated request synchronously on Qt's main thread. As describe below all +// IMF requests are made from another thread but need to be executed on the main thread. +static void executeIMFRequest(QQnxImfRequest *event) +{ + QMetaObject::invokeMethod(sInputContextInstance, + "processImfEvent", + Qt::BlockingQueuedConnection, + Q_ARG(QQnxImfRequest*, event)); +} + +// The following functions (ic_*) are callback functions called by the input system to query information +// about the text object that currently has focus or to make changes to it. All calls are made from the +// input system's own thread. The pattern for each callback function is to copy its parameters into +// a QQnxImfRequest structure and call executeIMFRequest to have it passed synchronously to Qt's main thread. +// Any return values should be pre-initialised with suitable default values as in some cases +// (e.g. a stale session) the call will return without having executed any request specific code. +// +// To make the correspondence more obvious, the names of these functions match those defined in the headers. +// They're in an anonymous namespace to avoid compiler conflicts with external functions defined with the +// same names. +namespace { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfBeginBatchEdit, &result); - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - int32_t ret = result.result().value<int32_t>(); +// See comment at beginning of namespace declaration for general information +static int32_t ic_begin_batch_edit(input_session_t *ic) +{ + Q_UNUSED(ic); - return ret; + // Ignore silently. + return 0; } -static int32_t imfClearMetaKeyStates(input_session_t *ic, int32_t states) +// End composition, committing the supplied text. +// See comment at beginning of namespace declaration for general information +static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfClearMetaKeyStates, &result); - iarg(states); - - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - int32_t ret = result.result().value<int32_t>(); + QQnxImfRequest event(ic, ImfCommitText); + event.ct.text = text; + event.ct.new_cursor_position = new_cursor_position; + event.ct.result = -1; + executeIMFRequest(&event); - return ret; + return event.ct.result; } -static int32_t imfCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +// Delete left_length characters before and right_length characters after the cursor. +// See comment at beginning of namespace declaration for general information +static int32_t ic_delete_surrounding_text(input_session_t *ic, int32_t left_length, int32_t right_length) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfCommitText, &result); - parg(text); - iarg(new_cursor_position); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest event(ic, ImfDeleteSurroundingText); + event.dst.left_length = left_length; + event.dst.right_length = right_length; + event.dst.result = -1; + executeIMFRequest(&event); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - - return ret; + return event.dst.result; } -static int32_t imfDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length) +// See comment at beginning of namespace declaration for general information +static int32_t ic_end_batch_edit(input_session_t *ic) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfDeleteSurroundingText, &result); - iarg(left_length); - iarg(right_length); + Q_UNUSED(ic); - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - int32_t ret = result.result().value<int32_t>(); - - return ret; + // Ignore silently. + return 0; } -static int32_t imfEndBatchEdit(input_session_t *ic) +// End composition, committing what's there. +// See comment at beginning of namespace declaration for general information +static int32_t ic_finish_composing_text(input_session_t *ic) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfEndBatchEdit, &result); - - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - int32_t ret = result.result().value<int32_t>(); + QQnxImfRequest event(ic, ImfFinishComposingText); + event.fct.result = -1; + executeIMFRequest(&event); - return ret; + return event.fct.result; } -static int32_t imfFinishComposingText(input_session_t *ic) +// Return the position of the cursor. +// See comment at beginning of namespace declaration for general information +static int32_t ic_get_cursor_position(input_session_t *ic) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfFinishComposingText, &result); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest event(ic, ImfGetCursorPosition); + event.gcp.result = -1; + executeIMFRequest(&event); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - - return ret; + return event.gcp.result; } -static int32_t imfGetCursorCapsMode(input_session_t *ic, int32_t req_modes) +// Return the n characters after the cursor. +// See comment at beginning of namespace declaration for general information +static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t n, int32_t flags) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfGetCursorCapsMode, &result); - iarg(req_modes); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest event(ic, ImfGetTextAfterCursor); + event.gtac.n = n; + event.gtac.flags = flags; + event.gtac.result = 0; + executeIMFRequest(&event); - int32_t ret = result.result().value<int32_t>(); - return ret; + return event.gtac.result; } -static int32_t imfGetCursorPosition(input_session_t *ic) +// Return the n characters before the cursor. +// See comment at beginning of namespace declaration for general information +static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_t n, int32_t flags) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfGetCursorPosition, &result); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest event(ic, ImfGetTextBeforeCursor); + event.gtac.n = n; + event.gtac.flags = flags; + event.gtac.result = 0; + executeIMFRequest(&event); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - - return ret; + return event.gtac.result; } -static extracted_text_t *imfGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags) +// Process an event from IMF. Primarily used for reflecting back keyboard events. +// See comment at beginning of namespace declaration for general information +static int32_t ic_send_event(input_session_t *ic, event_t *event) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - 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); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest imfEvent(ic, ImfSendEvent); + imfEvent.sae.event = event; + imfEvent.sae.result = -1; + executeIMFRequest(&imfEvent); - result.wait(); - return result.result().value<extracted_text_t *>(); + return imfEvent.sae.result; } -static spannable_string_t *imfGetSelectedText(input_session_t *ic, int32_t flags) +// Same as ic_send_event. +// See comment at beginning of namespace declaration for general information +static int32_t ic_send_async_event(input_session_t *ic, event_t *event) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return toSpannableString(""); - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfGetSelectedText, &result); - iarg(flags); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + // There's no difference from our point of view between ic_send_event & ic_send_async_event + QQnxImfRequest imfEvent(ic, ImfSendEvent); + imfEvent.sae.event = event; + imfEvent.sae.result = -1; + executeIMFRequest(&imfEvent); - result.wait(); - return result.result().value<extracted_text_t *>(); + return imfEvent.sae.result; } -static spannable_string_t *imfGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags) +// Set the range of text between start and end as the composition range. +// See comment at beginning of namespace declaration for general information +static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32_t end) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return toSpannableString(""); - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfGetTextAfterCursor, &result); - iarg(n); - iarg(flags); + QQnxImfRequest event(ic, ImfSetComposingRegion); + event.scr.start = start; + event.scr.end = end; + event.scr.result = -1; + executeIMFRequest(&event); - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - return result.result().value<extracted_text_t *>(); + return event.scr.result; } -static spannable_string_t *imfGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags) +// Update the composition range with the supplied text. This can be called when no composition +// range is in effect in which case one is started at the current cursor position. +// See comment at beginning of namespace declaration for general information +static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return toSpannableString(""); + QQnxImfRequest event(ic, ImfSetComposingText); + event.sct.text = text; + event.sct.new_cursor_position = new_cursor_position; + event.sct.result = -1; + executeIMFRequest(&event); - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfGetTextBeforeCursor, &result); - iarg(n); - iarg(flags); - - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - return result.result().value<extracted_text_t *>(); + return event.sct.result; } -static int32_t imfPerformEditorAction(input_session_t *ic, int32_t editor_action) +// Indicate if any text is selected +// See comment at beginning of namespace declaration for general information +static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfPerformEditorAction, &result); - iarg(editor_action); + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - QCoreApplication::postEvent(QCoreApplication::instance(), event); + QQnxImfRequest event(ic, ImfIsTextSelected); + event.its.pIsSelected = pIsSelected; + event.its.result = -1; + executeIMFRequest(&event); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - return ret; + return event.its.result; } -static int32_t imfReportFullscreenMode(input_session_t *ic, int32_t enabled) +// Indicate if all text is selected +// See comment at beginning of namespace declaration for general information +static int32_t ic_is_all_text_selected(input_session_t* ic, int32_t* pIsSelected) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; + qInputContextIMFRequestDebug() << Q_FUNC_INFO; - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfReportFullscreenMode, &result); - iarg(enabled); + QQnxImfRequest event(ic, ImfIsAllTextSelected); + event.its.pIsSelected = pIsSelected; + event.its.result = -1; + executeIMFRequest(&event); - QCoreApplication::postEvent(QCoreApplication::instance(), event); - - result.wait(); - int32_t ret = result.result().value<int32_t>(); - return ret; + return event.its.result; } -static int32_t imfSendEvent(input_session_t *ic, event_t *event) -{ - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEvent *imfEvent = new ImfEvent(ic, ImfSendEvent, 0); - imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event)); +// LCOV_EXCL_START - exclude from code coverage analysis +// The following functions are defined in the IMF headers but are not currently called. - QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent); +// Not currently used +static int32_t ic_perform_editor_action(input_session_t *ic, int32_t editor_action) +{ + Q_UNUSED(ic); + Q_UNUSED(editor_action); + qCritical() << "ic_perform_editor_action not implemented"; return 0; } -static int32_t imfSendAsyncEvent(input_session_t *ic, event_t *event) +// Not currently used +static int32_t ic_report_fullscreen_mode(input_session_t *ic, int32_t enabled) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - ImfEvent *imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0); - imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event)); - - QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent); + Q_UNUSED(ic); + Q_UNUSED(enabled); + qCritical() << "ic_report_fullscreen_mode not implemented"; return 0; } -static int32_t imfSetComposingRegion(input_session_t *ic, int32_t start, int32_t end) +// Not currently used +static extracted_text_t *ic_get_extracted_text(input_session_t *ic, extracted_text_request_t *request, int32_t flags) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; + Q_UNUSED(ic); + Q_UNUSED(request); + Q_UNUSED(flags); - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfSetComposingRegion, &result); - iarg(start); - iarg(end); + qCritical() << "ic_get_extracted_text not implemented"; + return 0; +} - QCoreApplication::postEvent(QCoreApplication::instance(), event); +// Not currently used +static spannable_string_t *ic_get_selected_text(input_session_t *ic, int32_t flags) +{ + Q_UNUSED(ic); + Q_UNUSED(flags); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - return ret; + qCritical() << "ic_get_selected_text not implemented"; + return 0; } -static int32_t imfSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +// Not currently used +static int32_t ic_get_cursor_caps_mode(input_session_t *ic, int32_t req_modes) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; + Q_UNUSED(ic); + Q_UNUSED(req_modes); - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfSetComposingText, &result); - parg(text); - iarg(new_cursor_position); + qCritical() << "ic_get_cursor_caps_mode not implemented"; + return 0; +} - QCoreApplication::postEvent(QCoreApplication::instance(), event); +// Not currently used +static int32_t ic_clear_meta_key_states(input_session_t *ic, int32_t states) +{ + Q_UNUSED(ic); + Q_UNUSED(states); - result.wait(); - int32_t ret = result.result().value<int32_t>(); - return ret; + qCritical() << "ic_clear_meta_key_states not implemented"; + return 0; } -static int32_t imfSetSelection(input_session_t *ic, int32_t start, int32_t end) +// Not currently used +static int32_t ic_set_selection(input_session_t *ic, int32_t start, int32_t end) { - qInputContextIMFEventDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; + Q_UNUSED(ic); + Q_UNUSED(start); + Q_UNUSED(end); - ImfEventResult result; - ImfEvent *event = new ImfEvent(ic, ImfSetSelection, &result); - iarg(start); - iarg(end); + qCritical() << "ic_set_selection not implemented"; + return 0; +} - QCoreApplication::postEvent(QCoreApplication::instance(), event); +// End of un-hittable code +// LCOV_EXCL_STOP - result.wait(); - int32_t ret = result.result().value<int32_t>(); - 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, + ic_begin_batch_edit, + ic_clear_meta_key_states, + ic_commit_text, + ic_delete_surrounding_text, + ic_end_batch_edit, + ic_finish_composing_text, + ic_get_cursor_caps_mode, + ic_get_cursor_position, + ic_get_extracted_text, + ic_get_selected_text, + ic_get_text_after_cursor, + ic_get_text_before_cursor, + ic_perform_editor_action, + ic_report_fullscreen_mode, + 0, //ic_send_key_event + ic_send_event, + ic_send_async_event, + ic_set_composing_region, + ic_set_composing_text, + ic_set_selection, + 0, //ic_set_candidates, + 0, //ic_get_cursor_offset, + 0, //ic_get_selection, + ic_is_text_selected, + ic_is_all_text_selected, + 0, //ic_get_max_cursor_offset_t }; +} // namespace + static void -initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId) +initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId, int eventSize) { 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)); + memset(pEvent, 0, eventSize); pEvent->event_type = eventType; pEvent->event_id = eventId; pEvent->pid = getpid(); @@ -566,31 +502,20 @@ initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, pEvent->transaction_id = ++s_transactionId; } -spannable_string_t *toSpannableString(const QString &text) +static spannable_string_t *toSpannableString(const QString &text) { qInputContextDebug() << Q_FUNC_INFO << text; - spannable_string_t *pString = reinterpret_cast<spannable_string_t *>(malloc(sizeof(spannable_string_t))); - pString->str = (wchar_t *)malloc(sizeof(wchar_t) * text.length() + 1); - pString->length = text.length(); + spannable_string_t *pString = static_cast<spannable_string_t *>(malloc(sizeof(spannable_string_t))); + pString->str = static_cast<wchar_t *>(malloc(sizeof(wchar_t) * text.length() + 1)); + pString->length = text.toWCharArray(pString->str); pString->spans = 0; pString->spans_count = 0; - - const QChar *pData = text.constData(); - wchar_t *pDst = pString->str; - - while (!pData->isNull()) - { - *pDst = pData->unicode(); - pDst++; - pData++; - } - *pDst = 0; + pString->str[pString->length] = 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; @@ -645,27 +570,30 @@ QT_BEGIN_NAMESPACE QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard) : QPlatformInputContext(), - m_lastCaretPos(0), + m_caretPosition(0), m_isComposing(false), + m_isUpdatingText(false), m_inputPanelVisible(false), m_inputPanelLocale(QLocale::c()), + m_focusObject(0), m_integration(integration), - m_virtualKeyboad(keyboard) + m_virtualKeyboard(keyboard) { qInputContextDebug() << Q_FUNC_INFO; if (!imfAvailable()) return; - if ( p_imf_client_init() != 0 ) { + // Save a pointer to ourselves so we can execute calls from IMF through executeIMFRequest + // In practice there will only ever be a single instance. + Q_ASSERT(sInputContextInstance == 0); + sInputContextInstance = this; + + 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(); - connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool))); connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale))); keyboardVisibilityChanged(keyboard.isVisible()); @@ -676,168 +604,75 @@ QQnxInputContext::~QQnxInputContext() { qInputContextDebug() << Q_FUNC_INFO; + Q_ASSERT(sInputContextInstance == this); + sInputContextInstance = 0; + 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 QQnxInputContext::isValid() const { return imfAvailable(); } -bool QQnxInputContext::eventFilter(QObject *obj, QEvent *event) +void QQnxInputContext::processImfEvent(QQnxImfRequest *imfEvent) { - if (event->type() == ImfEvent::sUserEventType) { - // Forward the event to our real handler. - ImfEvent *imfEvent = static_cast<ImfEvent *>(event); - switch (imfEvent->m_imfType) { - case ImfBeginBatchEdit: { - int32_t ret = onBeginBatchEdit(imfEvent->m_session); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfClearMetaKeyStates: { - getarg(int32_t, states); - int32_t ret = onClearMetaKeyStates(imfEvent->m_session, states); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfCommitText: { - getparg(spannable_string_t*, text); - getarg(int32_t, new_cursor_position); - int32_t ret = onCommitText(imfEvent->m_session, text, new_cursor_position); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfDeleteSurroundingText: { - getarg(int32_t, left_length); - getarg(int32_t, right_length); - int32_t ret = onDeleteSurroundingText(imfEvent->m_session, left_length, right_length); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfEndBatchEdit: { - int32_t ret = onEndBatchEdit(imfEvent->m_session); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfFinishComposingText: { - int32_t ret = onFinishComposingText(imfEvent->m_session); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfGetCursorCapsMode: { - getarg(int32_t, req_modes); - int32_t ret = onGetCursorCapsMode(imfEvent->m_session, req_modes); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfGetCursorPosition: { - int32_t ret = onGetCursorPosition(imfEvent->m_session); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - - case ImfGetExtractedText: { - getparg(extracted_text_request_t*, request); - getarg(int32_t, flags); - extracted_text_t *ret = onGetExtractedText(imfEvent->m_session, request, flags); - imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret))); - break; - } + // If input session is no longer current, just bail, imfEvent should already be set with the appropriate + // return value. The only exception is spell check events since they're not associated with the + // object with focus. + if (imfEvent->type != ImfSendEvent || imfEvent->sae.event->event_type != EVENT_SPELL_CHECK) { + if (!isSessionOkay(imfEvent->session)) + return; + } - case ImfGetSelectedText: { - getarg(int32_t, flags); - spannable_string_t *ret = onGetSelectedText(imfEvent->m_session, flags); - imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret))); - break; - } + switch (imfEvent->type) { + case ImfCommitText: + imfEvent->ct.result = onCommitText(imfEvent->ct.text, imfEvent->ct.new_cursor_position); + break; - case ImfGetTextAfterCursor: { - getarg(int32_t, n); - getarg(int32_t, flags); - spannable_string_t *ret = onGetTextAfterCursor(imfEvent->m_session, n, flags); - imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret))); - break; - } + case ImfDeleteSurroundingText: + imfEvent->dst.result = onDeleteSurroundingText(imfEvent->dst.left_length, imfEvent->dst.right_length); + break; - case ImfGetTextBeforeCursor: { - getarg(int32_t, n); - getarg(int32_t, flags); - spannable_string_t *ret = onGetTextBeforeCursor(imfEvent->m_session, n, flags); - imfEvent->m_result->setResult(QVariant::fromValue((void*)ret)); - break; - } + case ImfFinishComposingText: + imfEvent->fct.result = onFinishComposingText(); + break; - case ImfPerformEditorAction: { - getarg(int32_t, editor_action); - int32_t ret = onPerformEditorAction(imfEvent->m_session, editor_action); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } + case ImfGetCursorPosition: + imfEvent->gcp.result = onGetCursorPosition(); + break; - case ImfReportFullscreenMode: { - getarg(int32_t, enabled); - int32_t ret = onReportFullscreenMode(imfEvent->m_session, enabled); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } + case ImfGetTextAfterCursor: + imfEvent->gtac.result = onGetTextAfterCursor(imfEvent->gtac.n, imfEvent->gtac.flags); + break; - case ImfSendEvent: { - getparg(event_t*, event); - onSendEvent(imfEvent->m_session, event); - break; - } + case ImfGetTextBeforeCursor: + imfEvent->gtac.result = onGetTextBeforeCursor(imfEvent->gtac.n, imfEvent->gtac.flags); + break; - case ImfSendAsyncEvent: { - getparg(event_t*, event); - onSendAsyncEvent(imfEvent->m_session, event); - break; - } + case ImfSendEvent: + imfEvent->sae.result = onSendEvent(imfEvent->sae.event); + break; - case ImfSetComposingRegion: { - getarg(int32_t, start); - getarg(int32_t, end); - int32_t ret = onSetComposingRegion(imfEvent->m_session, start, end); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } + case ImfSetComposingRegion: + imfEvent->scr.result = onSetComposingRegion(imfEvent->scr.start, imfEvent->scr.end); + break; - case ImfSetComposingText: { - getparg(spannable_string_t*, text); - getarg(int32_t, new_cursor_position); - int32_t ret = onSetComposingText(imfEvent->m_session, text, new_cursor_position); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } + case ImfSetComposingText: + imfEvent->sct.result = onSetComposingText(imfEvent->sct.text, imfEvent->sct.new_cursor_position); + break; - case ImfSetSelection: { - getarg(int32_t, start); - getarg(int32_t, end); - int32_t ret = onSetSelection(imfEvent->m_session, start, end); - imfEvent->m_result->setResult(QVariant::fromValue(ret)); - break; - } - }; //switch + case ImfIsTextSelected: + imfEvent->its.result = onIsTextSelected(imfEvent->its.pIsSelected); + break; - return true; - } else { - // standard event processing - return QObject::eventFilter(obj, event); - } + case ImfIsAllTextSelected: + imfEvent->its.result = onIsAllTextSelected(imfEvent->its.pIsSelected); + break; + }; //switch } bool QQnxInputContext::filterEvent( const QEvent *event ) @@ -845,12 +680,12 @@ bool QQnxInputContext::filterEvent( const QEvent *event ) qInputContextDebug() << Q_FUNC_INFO << event; switch (event->type()) { - case QEvent::CloseSoftwareInputPanel: { + case QEvent::CloseSoftwareInputPanel: return dispatchCloseSoftwareInputPanel(); - } - case QEvent::RequestSoftwareInputPanel: { + + case QEvent::RequestSoftwareInputPanel: return dispatchRequestSoftwareInputPanel(); - } + default: return false; } @@ -869,12 +704,30 @@ void QQnxInputContext::reset() endComposition(); } -void QQnxInputContext::update(Qt::InputMethodQueries queries) +void QQnxInputContext::commit() { qInputContextDebug() << Q_FUNC_INFO; - reset(); + endComposition(); +} - QPlatformInputContext::update(queries); +void QQnxInputContext::update(Qt::InputMethodQueries queries) +{ + qInputContextDebug() << Q_FUNC_INFO << queries; + + if (queries & Qt::ImCursorPosition) { + int lastCaret = m_caretPosition; + updateCursorPosition(); + // If caret position has changed we need to inform IMF unless this is just due to our own action + // such as committing text. + if (hasSession() && !m_isUpdatingText && lastCaret != m_caretPosition) { + caret_event_t caretEvent; + initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED, sizeof(caretEvent)); + caretEvent.old_pos = lastCaret; + caretEvent.new_pos = m_caretPosition; + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event caret changed" << lastCaret << m_caretPosition; + p_ictrl_dispatch_event(&caretEvent.event); + } + } } void QQnxInputContext::closeSession() @@ -887,16 +740,23 @@ void QQnxInputContext::closeSession() p_ictrl_close_session((input_session_t *)sInputSession); sInputSession = 0; } + // These are likely already in the right state but this depends on the text control + // having called reset or commit. So, just in case, set them to proper values. + m_isComposing = false; + m_composingText.clear(); } -void QQnxInputContext::openSession() +bool QQnxInputContext::openSession() { - qInputContextDebug() << Q_FUNC_INFO; if (!imfAvailable()) - return; + return false; closeSession(); sInputSession = p_ictrl_open_session(&ic_funcs); + + qInputContextDebug() << Q_FUNC_INFO; + + return sInputSession != 0; } bool QQnxInputContext::hasSession() @@ -918,79 +778,89 @@ bool QQnxInputContext::hasSelectedText() bool QQnxInputContext::dispatchRequestSoftwareInputPanel() { + qInputContextDebug() << Q_FUNC_INFO << "requesting keyboard" << m_inputPanelVisible; m_virtualKeyboard.showKeyboard(); - qInputContextDebug() << Q_FUNC_INFO << "requesting virtual keyboard"; - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input || !inputMethodAccepted()) - return true; - - if (!hasSession()) - openSession(); - // This also means that the caret position has moved - QInputMethodQueryEvent query(Qt::ImCursorPosition); - QCoreApplication::sendEvent(input, &query); - int caretPos = query.value(Qt::ImCursorPosition).toInt(); - caret_event_t caretEvent; - memset(&caretEvent, 0, sizeof(caret_event_t)); - initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED); - caretEvent.old_pos = m_lastCaretPos; - m_lastCaretPos = caretEvent.new_pos = caretPos; - p_ictrl_dispatch_event((event_t *)&caretEvent); return true; } bool QQnxInputContext::dispatchCloseSoftwareInputPanel() { + qInputContextDebug() << Q_FUNC_INFO << "hiding keyboard" << m_inputPanelVisible; m_virtualKeyboard.hideKeyboard(); - qInputContextDebug() << Q_FUNC_INFO << "hiding virtual keyboard"; - // This also means we are stopping composition, but we should already have done that. return true; } /** * IMF Event Dispatchers. */ -bool QQnxInputContext::dispatchFocusEvent(FocusEventId id, int hints) +bool QQnxInputContext::dispatchFocusGainEvent(int inputHints) { - qInputContextDebug() << Q_FUNC_INFO; + if (hasSession()) + dispatchFocusLossEvent(); - if (!sInputSession) { - qWarning() << Q_FUNC_INFO << "Attempt to dispatch a focus event with no input session."; - return false; - } + QObject *input = qGuiApp->focusObject(); - if (!imfAvailable()) + if (!input || !openSession()) return false; // Set the last caret position to 0 since we don't really have one and we don't // want to have the old one. - m_lastCaretPos = 0; + m_caretPosition = 0; + + QInputMethodQueryEvent query(Qt::ImHints); + QCoreApplication::sendEvent(input, &query); focus_event_t focusEvent; - memset(&focusEvent, 0, sizeof(focusEvent)); - initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, id); + initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_GAINED, sizeof(focusEvent)); focusEvent.style = DEFAULT_STYLE; - if (hints && Qt::ImhNoPredictiveText) + if (inputHints & Qt::ImhNoPredictiveText) focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION; - if (hints && Qt::ImhNoAutoUppercase) + if (inputHints & Qt::ImhNoAutoUppercase) focusEvent.style |= NO_AUTO_TEXT; + // Following styles are mutually exclusive + if (inputHints & Qt::ImhHiddenText) { + focusEvent.style |= IMF_PASSWORD_TYPE; + } else if (inputHints & Qt::ImhDialableCharactersOnly) { + focusEvent.style |= IMF_PHONE_TYPE; + } else if (inputHints & Qt::ImhUrlCharactersOnly) { + focusEvent.style |= IMF_URL_TYPE; + } else if (inputHints & Qt::ImhEmailCharactersOnly) { + focusEvent.style |= IMF_EMAIL_TYPE; + } + + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus gain style:" << focusEvent.style; + p_ictrl_dispatch_event((event_t *)&focusEvent); return true; } -bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap) +void QQnxInputContext::dispatchFocusLossEvent() { - if (!imfAvailable()) + if (hasSession()) { + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus lost"; + + focus_event_t focusEvent; + initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_LOST, sizeof(focusEvent)); + p_ictrl_dispatch_event((event_t *)&focusEvent); + closeSession(); + } +} + +bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId) +{ + Q_UNUSED(scan); + + if (!hasSession()) return false; int key = (flags & KEY_SYM_VALID) ? sym : cap; - bool navKey = false; - switch ( key ) { + bool navigationKey = false; + switch (key) { case KEYCODE_RETURN: /* In a single line edit we should end composition because enter might be used by something. endComposition(); @@ -1007,27 +877,22 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan break; case KEYCODE_LEFT: key = NAVIGATE_LEFT; - navKey = true; + navigationKey = true; break; case KEYCODE_RIGHT: key = NAVIGATE_RIGHT; - navKey = true; + navigationKey = true; break; case KEYCODE_UP: key = NAVIGATE_UP; - navKey = true; + navigationKey = true; break; case KEYCODE_DOWN: key = NAVIGATE_DOWN; - navKey = true; + navigationKey = 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: @@ -1041,85 +906,183 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan 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 ) { + if (navigationKey) { // 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); + initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key, sizeof(navEvent)); navEvent.magnitude = 1; - qInputContextDebug() << Q_FUNC_INFO << "dispatch navigation event " << key; + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even navigation" << key; p_ictrl_dispatch_event(&navEvent.event); } - } - else { + } 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; + initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP, + sizeof(keyEvent)); + keyEvent.key_code = cap; + keyEvent.character = sym; + keyEvent.meta_key_state = mod; + keyEvent.sequence_id = sequenceId; p_ictrl_dispatch_event(&keyEvent.event); - qInputContextDebug() << Q_FUNC_INFO << "dispatch key event " << key; + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even key" << key; } - scan = 0; return true; } -void QQnxInputContext::endComposition() +void QQnxInputContext::updateCursorPosition() { - if (!m_isComposing) + QObject *input = qGuiApp->focusObject(); + if (!input) return; - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) + QInputMethodQueryEvent query(Qt::ImCursorPosition); + QCoreApplication::sendEvent(input, &query); + m_caretPosition = query.value(Qt::ImCursorPosition).toInt(); + + qInputContextDebug() << Q_FUNC_INFO << m_caretPosition; +} + +void QQnxInputContext::endComposition() +{ + if (!m_isComposing) return; - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(QLatin1String(""), attributes); - event.setCommitString(m_composingText); - m_composingText = QString(); - m_isComposing = false; - QCoreApplication::sendEvent(input, &event); + finishComposingText(); - action_event_t actionEvent; - memset(&actionEvent, 0, sizeof(actionEvent)); - initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION); - p_ictrl_dispatch_event(&actionEvent.event); + if (hasSession()) { + action_event_t actionEvent; + initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION, sizeof(actionEvent)); + qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even end composition"; + p_ictrl_dispatch_event(&actionEvent.event); + } } -void QQnxInputContext::setComposingText(QString const& composingText) +void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_cursor_position) { - m_composingText = composingText; + QObject *input = qGuiApp->focusObject(); + if (!input) + return; + + if (new_cursor_position > 0) + new_cursor_position += text->length - 1; + + m_composingText = QString::fromWCharArray(text->str, text->length); m_isComposing = true; - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return; + qInputContextDebug() << Q_FUNC_INFO << m_composingText << new_cursor_position; QList<QInputMethodEvent::Attribute> attributes; - QTextCharFormat format; - format.setFontUnderline(true); - attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format)); + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, + new_cursor_position, + 1, + QVariant())); + + for (unsigned int i = 0; i < text->spans_count; ++i) { + QColor highlightColor; + bool underline = false; + + if ((text->spans[i].attributes_mask & COMPOSED_TEXT_ATTRIB) != 0) + underline = true; + + if ((text->spans[i].attributes_mask & ACTIVE_REGION_ATTRIB) != 0) { + underline = true; + highlightColor = m_highlightColor[ActiveRegion]; + } else if ((text->spans[i].attributes_mask & AUTO_CORRECTION_ATTRIB) != 0) { + highlightColor = m_highlightColor[AutoCorrected]; + } else if ((text->spans[i].attributes_mask & REVERT_CORRECTION_ATTRIB) != 0) { + highlightColor = m_highlightColor[Reverted]; + } - QInputMethodEvent event(composingText, attributes); + if (underline || highlightColor.isValid()) { + QTextCharFormat format; + if (underline) + format.setFontUnderline(true); + if (highlightColor.isValid()) + format.setBackground(QBrush(highlightColor)); + qInputContextDebug() << " attrib: " << underline << highlightColor << text->spans[i].start << text->spans[i].end; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, text->spans[i].start, + text->spans[i].end - text->spans[i].start + 1, QVariant(format))); + } + } + QInputMethodEvent event(m_composingText, attributes); + m_isUpdatingText = true; QCoreApplication::sendEvent(input, &event); + m_isUpdatingText = false; + + updateCursorPosition(); +} + +void QQnxInputContext::finishComposingText() +{ + QObject *input = qGuiApp->focusObject(); + + if (input) { + qInputContextDebug() << Q_FUNC_INFO << m_composingText; + + QInputMethodEvent event; + event.setCommitString(m_composingText); + m_isUpdatingText = true; + QCoreApplication::sendEvent(input, &event); + m_isUpdatingText = false; + } + m_composingText = QString(); + m_isComposing = false; + + updateCursorPosition(); +} + +// Return the index relative to a UTF-16 sequence of characters for a index that is relative to the +// corresponding UTF-32 character string given a starting index in the UTF-16 string and a count +// of the number of lead surrogates prior to that index. Updates the highSurrogateCount to reflect the +// new surrogate characters encountered. +static int adjustIndex(const QChar *text, int utf32Index, int utf16StartIndex, int *highSurrogateCount) +{ + int utf16Index = utf32Index + *highSurrogateCount; + while (utf16StartIndex < utf16Index) { + if (text[utf16StartIndex].isHighSurrogate()) { + ++utf16Index; + ++*highSurrogateCount; + } + ++utf16StartIndex; + } + return utf16StartIndex; +} + +int QQnxInputContext::handleSpellCheck(spell_check_event_t *event) +{ + // These should never happen. + if (sSpellCheckQueue->isEmpty() || event->event.event_id != NOTIFY_SP_CHECK_MISSPELLINGS) + return -1; + + SpellCheckInfo callerInfo = sSpellCheckQueue->dequeue(); + spannable_string_t* spellCheckData = *event->data; + QString text = QString::fromWCharArray(spellCheckData->str, spellCheckData->length); + // Generate the list of indices indicating misspelled words in the text. We use end + 1 + // since it's more conventional to have the end index point just past the string. We also + // can't use the indices directly since they are relative to UTF-32 encoded data and the + // conversion to Qt's UTF-16 internal format might cause lengthening. + QList<int> indices; + int adjustment = 0; + int index = 0; + for (unsigned int i = 0; i < spellCheckData->spans_count; ++i) { + if (spellCheckData->spans[i].attributes_mask & MISSPELLED_WORD_ATTRIB) { + index = adjustIndex(text.data(), spellCheckData->spans[i].start, index, &adjustment); + indices.push_back(index); + index = adjustIndex(text.data(), spellCheckData->spans[i].end + 1, index, &adjustment); + indices.push_back(index); + } + } + callerInfo.spellCheckDone(callerInfo.context, text, indices); + + return 0; } int32_t QQnxInputContext::processEvent(event_t *event) @@ -1128,7 +1091,7 @@ int32_t QQnxInputContext::processEvent(event_t *event) switch (event->event_type) { case EVENT_SPELL_CHECK: { qInputContextDebug() << Q_FUNC_INFO << "EVENT_SPELL_CHECK"; - result = 0; + result = handleSpellCheck(reinterpret_cast<spell_check_event_t *>(event)); break; } @@ -1140,19 +1103,24 @@ int32_t QQnxInputContext::processEvent(event_t *event) event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT : event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0; - QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0); - QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0); + QQnxScreenEventHandler::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0); + QQnxScreenEventHandler::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0); result = 0; break; } case EVENT_KEY: { - qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY"; - key_event_t *kevent = static_cast<key_event_t *>(event); - - QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code); - QQnxEventThread::injectKeyboardEvent(KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code); - + key_event_t *kevent = reinterpret_cast<key_event_t *>(event); + int keySym = kevent->character != 0 ? kevent->character : kevent->key_code; + int keyCap = kevent->key_code; + int modifiers = 0; + if (kevent->meta_key_state & META_SHIFT_ON) + modifiers |= KEYMOD_SHIFT; + int flags = KEY_SYM_VALID | KEY_CAP_VALID; + if (event->event_id == IMF_KEY_DOWN) + flags |= KEY_DOWN; + qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY" << flags << keySym; + QQnxScreenEventHandler::injectKeyboardEvent(flags, keySym, modifiers, 0, keyCap); result = 0; break; } @@ -1179,339 +1147,175 @@ int32_t QQnxInputContext::processEvent(event_t *event) * IMF Event Handlers */ -int32_t QQnxInputContext::onBeginBatchEdit(input_session_t *ic) -{ - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - // We don't care. - return 0; -} - -int32_t QQnxInputContext::onClearMetaKeyStates(input_session_t *ic, int32_t states) -{ - Q_UNUSED(states); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - // Should never get called. - qCritical() << Q_FUNC_INFO << "onClearMetaKeyStates is unsupported."; - return 0; -} - -int32_t QQnxInputContext::onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +int32_t QQnxInputContext::onCommitText(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; - - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return 0; - - QString commitString = QString::fromWCharArray(text->str, text->length); - - qInputContextDebug() << Q_FUNC_INFO << "Committing [" << commitString << "]"; - - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(QLatin1String(""), attributes); - event.setCommitString(commitString, 0, 0); + Q_UNUSED(new_cursor_position); - QCoreApplication::sendEvent(input, &event); - m_composingText = QString(); + updateComposition(text, new_cursor_position); + finishComposingText(); return 0; } -int32_t QQnxInputContext::onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length) +int32_t QQnxInputContext::onDeleteSurroundingText(int32_t left_length, int32_t right_length) { qInputContextDebug() << Q_FUNC_INFO << "L:" << left_length << " R:" << right_length; - if (!isSessionOkay(ic)) - return 0; - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return 0; - - if (hasSelectedText()) { - QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0); - QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0); - reset(); + if (!input) 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); - QCoreApplication::sendEvent(input, &event); - - return 0; -} - -int32_t QQnxInputContext::onEndBatchEdit(input_session_t *ic) -{ - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - return 0; -} - -int32_t QQnxInputContext::onFinishComposingText(input_session_t *ic) -{ - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return 0; + finishComposingText(); - // 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(m_composingText); - m_composingText = QString(); - m_isComposing = false; + QInputMethodEvent event; + event.setCommitString(QString(), replacementStart, replacementLength); + m_isUpdatingText = true; QCoreApplication::sendEvent(input, &event); + m_isUpdatingText = false; - return 0; -} - -int32_t QQnxInputContext::onGetCursorCapsMode(input_session_t *ic, int32_t req_modes) -{ - Q_UNUSED(req_modes); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - // Should never get called. - qCritical() << Q_FUNC_INFO << "onGetCursorCapsMode is unsupported."; + updateCursorPosition(); return 0; } -int32_t QQnxInputContext::onGetCursorPosition(input_session_t *ic) +int32_t QQnxInputContext::onFinishComposingText() { - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; + finishComposingText(); - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return 0; - - QInputMethodQueryEvent query(Qt::ImCursorPosition); - QCoreApplication::sendEvent(input, &query); - m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); - - return m_lastCaretPos; -} - -extracted_text_t *QQnxInputContext::onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags) -{ - Q_UNUSED(flags); - Q_UNUSED(request); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) { - extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1); - et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1)); - return et; - } - - // Used to update dictionaries, but not supported right now. - extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1); - et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1)); - - return et; + return 0; } -spannable_string_t *QQnxInputContext::onGetSelectedText(input_session_t *ic, int32_t flags) +int32_t QQnxInputContext::onGetCursorPosition() { - Q_UNUSED(flags); qInputContextDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return toSpannableString(""); - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) + if (!input) return 0; - QInputMethodQueryEvent query(Qt::ImCurrentSelection); - QCoreApplication::sendEvent(input, &query); - QString text = query.value(Qt::ImCurrentSelection).toString(); + updateCursorPosition(); - return toSpannableString(text); + return m_caretPosition; } -spannable_string_t *QQnxInputContext::onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags) +spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t flags) { Q_UNUSED(flags); qInputContextDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return toSpannableString(""); - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) + if (!input) return toSpannableString(""); QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); QCoreApplication::sendEvent(input, &query); QString text = query.value(Qt::ImSurroundingText).toString(); - m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + m_caretPosition = query.value(Qt::ImCursorPosition).toInt(); - return toSpannableString(text.mid(m_lastCaretPos+1, n)); + return toSpannableString(text.mid(m_caretPosition, n)); } -spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags) +spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t flags) { Q_UNUSED(flags); qInputContextDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return toSpannableString(""); - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) + if (!input) return toSpannableString(""); QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); QCoreApplication::sendEvent(input, &query); QString text = query.value(Qt::ImSurroundingText).toString(); - m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + m_caretPosition = query.value(Qt::ImCursorPosition).toInt(); - if (n < m_lastCaretPos) - return toSpannableString(text.mid(m_lastCaretPos - n, n)); + if (n < m_caretPosition) + return toSpannableString(text.mid(m_caretPosition - n, n)); else - return toSpannableString(text.mid(0, m_lastCaretPos)); -} - -int32_t QQnxInputContext::onPerformEditorAction(input_session_t *ic, int32_t editor_action) -{ - Q_UNUSED(editor_action); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - // Should never get called. - qCritical() << Q_FUNC_INFO << "onPerformEditorAction is unsupported."; - - return 0; -} - -int32_t QQnxInputContext::onReportFullscreenMode(input_session_t *ic, int32_t enabled) -{ - Q_UNUSED(enabled); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - // Should never get called. - qCritical() << Q_FUNC_INFO << "onReportFullscreenMode is unsupported."; - - return 0; -} - -int32_t QQnxInputContext::onSendEvent(input_session_t *ic, event_t *event) -{ - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - return processEvent(event); + return toSpannableString(text.mid(0, m_caretPosition)); } -int32_t QQnxInputContext::onSendAsyncEvent(input_session_t *ic, event_t *event) +int32_t QQnxInputContext::onSendEvent(event_t *event) { qInputContextDebug() << Q_FUNC_INFO; - if (!isSessionOkay(ic)) - return 0; - return processEvent(event); } -int32_t QQnxInputContext::onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end) +int32_t QQnxInputContext::onSetComposingRegion(int32_t start, int32_t end) { - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) + if (!input) return 0; QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); QCoreApplication::sendEvent(input, &query); QString text = query.value(Qt::ImSurroundingText).toString(); - m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); + m_caretPosition = query.value(Qt::ImCursorPosition).toInt(); - QString empty = QString::fromLatin1(""); - text = text.mid(start, end - start); + qInputContextDebug() << Q_FUNC_INFO << text; + + m_isUpdatingText = true; // Delete the current text. + QInputMethodEvent deleteEvent; + deleteEvent.setCommitString(QString(), start - m_caretPosition, end - start); + QCoreApplication::sendEvent(input, &deleteEvent); + + m_composingText = text.mid(start, end - start); + m_isComposing = true; + QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(empty, attributes); - event.setCommitString(empty, start - m_lastCaretPos, end - start); - QCoreApplication::sendEvent(input, &event); + QTextCharFormat format; + format.setFontUnderline(true); + attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, m_composingText.length(), format)); + + QInputMethodEvent setTextEvent(m_composingText, attributes); + QCoreApplication::sendEvent(input, &setTextEvent); - // Move the specified text into a preedit string. - setComposingText(text); + m_isUpdatingText = false; return 0; } -int32_t QQnxInputContext::onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position) +int32_t QQnxInputContext::onSetComposingText(spannable_string_t *text, int32_t new_cursor_position) { - Q_UNUSED(new_cursor_position); - qInputContextDebug() << Q_FUNC_INFO; - - if (!isSessionOkay(ic)) - return 0; - - QObject *input = qGuiApp->focusObject(); - if (!imfAvailable() || !input) - return 0; + if (text->length > 0) { + updateComposition(text, new_cursor_position); + } else { + // If the composing text is empty we can simply end composition, the visual effect is the same. + // However, sometimes one wants to display hint text in an empty text field and for this to work + // QQuickTextEdit.inputMethodComposing has to be false if the composition string is empty. + m_composingText.clear(); + finishComposingText(); + } + return 0; +} - m_isComposing = true; +int32_t QQnxInputContext::onIsTextSelected(int32_t* pIsSelected) +{ + *pIsSelected = hasSelectedText(); - QString preeditString = QString::fromWCharArray(text->str, text->length); - setComposingText(preeditString); + qInputContextDebug() << Q_FUNC_INFO << *pIsSelected; return 0; } -int32_t QQnxInputContext::onSetSelection(input_session_t *ic, int32_t start, int32_t end) +int32_t QQnxInputContext::onIsAllTextSelected(int32_t* pIsSelected) { - Q_UNUSED(start); - Q_UNUSED(end); - qInputContextDebug() << Q_FUNC_INFO; + QObject *input = qGuiApp->focusObject(); + if (!input) + return -1; - if (!isSessionOkay(ic)) - return 0; + QInputMethodQueryEvent query(Qt::ImCurrentSelection | Qt::ImSurroundingText); + QCoreApplication::sendEvent(input, &query); + + *pIsSelected = query.value(Qt::ImSurroundingText).toString().length() == query.value(Qt::ImCurrentSelection).toString().length(); - // Should never get called. - qCritical() << Q_FUNC_INFO << "onSetSelection is unsupported."; + qInputContextDebug() << Q_FUNC_INFO << *pIsSelected; return 0; } @@ -1556,19 +1360,74 @@ void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale) } } +void QQnxInputContext::setHighlightColor(int index, const QColor &color) +{ + qInputContextDebug() << Q_FUNC_INFO << "setHighlightColor" << index << color << qGuiApp->focusObject(); + + if (!sInputContextInstance) + return; + + // If the focus has changed, revert all colors to the default. + if (sInputContextInstance->m_focusObject != qGuiApp->focusObject()) { + QColor invalidColor; + sInputContextInstance->m_highlightColor[ActiveRegion] = sSelectedColor; + sInputContextInstance->m_highlightColor[AutoCorrected] = invalidColor; + sInputContextInstance->m_highlightColor[Reverted] = invalidColor; + sInputContextInstance->m_focusObject = qGuiApp->focusObject(); + } + if (index >= 0 && index <= Reverted) + sInputContextInstance->m_highlightColor[index] = color; +} + void QQnxInputContext::setFocusObject(QObject *object) { qInputContextDebug() << Q_FUNC_INFO << "input item=" << object; + // Ensure the colors are reset if we've a change in focus object + setHighlightColor(-1, QColor()); + if (!inputMethodAccepted()) { if (m_inputPanelVisible) hideInputPanel(); + if (hasSession()) + dispatchFocusLossEvent(); } else { - m_virtualKeyboard.setInputHintsFromObject(object); + QInputMethodQueryEvent query(Qt::ImHints); + QCoreApplication::sendEvent(object, &query); + int inputHints = query.value(Qt::ImHints).toInt(); + + dispatchFocusGainEvent(inputHints); + + m_virtualKeyboard.setInputHints(inputHints); if (!m_inputPanelVisible) showInputPanel(); } } +bool QQnxInputContext::checkSpelling(const QString &text, void *context, void (*spellCheckDone)(void *context, const QString &text, const QList<int> &indices)) +{ + qInputContextDebug() << Q_FUNC_INFO << "text" << text; + + if (!imfAvailable()) + return false; + + if (!sSpellCheckSession) + sSpellCheckSession = p_ictrl_open_session(&ic_funcs); + + action_event_t spellEvent; + initEvent(&spellEvent.event, sSpellCheckSession, EVENT_ACTION, ACTION_CHECK_MISSPELLINGS, sizeof(spellEvent)); + int len = text.length(); + spellEvent.event_data = alloca(sizeof(wchar_t) * (len + 1)); + spellEvent.length_data = text.toWCharArray(static_cast<wchar_t*>(spellEvent.event_data)) * sizeof(wchar_t); + + int rc = p_ictrl_dispatch_event(reinterpret_cast<event_t*>(&spellEvent)); + + if (rc == 0) { + sSpellCheckQueue->enqueue(SpellCheckInfo(context, spellCheckDone)); + return true; + } + return false; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.h b/src/plugins/platforms/qnx/qqnxinputcontext_imf.h index 1980a99ed9..5028215bbe 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.h +++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.h @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -43,9 +43,11 @@ #define QQNXINPUTCONTEXT_H #include <qpa/qplatforminputcontext.h> +#include "qqnxscreeneventfilter.h" #include <QtCore/QLocale> #include <QtCore/QMetaType> +#include <QtCore/QList> #include <qpa/qplatformintegration.h> #include "imf/imf_client.h" @@ -55,21 +57,30 @@ QT_BEGIN_NAMESPACE class QQnxAbstractVirtualKeyboard; class QQnxIntegration; +class QQnxImfRequest; -class QQnxInputContext : public QPlatformInputContext +class QQnxInputContext : public QPlatformInputContext, public QQnxScreenEventFilter { Q_OBJECT public: explicit QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard); ~QQnxInputContext(); + // Indices for selecting and setting highlight colors. + enum HighlightIndex { + ActiveRegion, + AutoCorrected, + Reverted, + }; + bool isValid() const; bool filterEvent(const QEvent *event); QRectF keyboardRect() const; void reset(); + void commit(); void update(Qt::InputMethodQueries); - bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId); void showInputPanel(); @@ -79,61 +90,62 @@ public: QLocale locale() const; void setFocusObject(QObject *object); -protected: - // Filters only for IMF events. - bool eventFilter(QObject *obj, QEvent *event); + static void setHighlightColor(int index, const QColor &color); + + static bool checkSpelling(const QString &text, void *context, void (*spellCheckDone)(void *context, const QString &text, const QList<int> &indices)); private Q_SLOTS: void keyboardVisibilityChanged(bool visible); void keyboardLocaleChanged(const QLocale &locale); + void processImfEvent(QQnxImfRequest *event); private: // IMF Event dispatchers - bool dispatchFocusEvent(FocusEventId id, int hints = Qt::ImhNone); + bool dispatchFocusGainEvent(int inputHints); + void dispatchFocusLossEvent(); bool dispatchRequestSoftwareInputPanel(); bool dispatchCloseSoftwareInputPanel(); + int handleSpellCheck(spell_check_event_t *event); int32_t processEvent(event_t *event); void closeSession(); - void openSession(); + bool openSession(); bool hasSession(); + void updateCursorPosition(); void endComposition(); - void setComposingText(QString const &composingText); + void finishComposingText(); bool hasSelectedText(); + void updateComposition(spannable_string_t *text, int32_t new_cursor_position); // 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 onCommitText(spannable_string_t *text, int32_t new_cursor_position); + int32_t onDeleteSurroundingText(int32_t left_length, int32_t right_length); + int32_t onGetCursorCapsMode(int32_t req_modes); + int32_t onFinishComposingText(); + int32_t onGetCursorPosition(); + spannable_string_t *onGetTextAfterCursor(int32_t n, int32_t flags); + spannable_string_t *onGetTextBeforeCursor(int32_t n, int32_t flags); + int32_t onSendEvent(event_t *event); + int32_t onSetComposingRegion(int32_t start, int32_t end); + int32_t onSetComposingText(spannable_string_t *text, int32_t new_cursor_position); + int32_t onIsTextSelected(int32_t* pIsSelected); + int32_t onIsAllTextSelected(int32_t* pIsSelected); int32_t onForceUpdate(); - int m_lastCaretPos; + int m_caretPosition; bool m_isComposing; QString m_composingText; + bool m_isUpdatingText; bool m_inputPanelVisible; QLocale m_inputPanelLocale; + // The object that had focus when the last highlight color was set. + QObject *m_focusObject; + // Indexed by HighlightIndex + QColor m_highlightColor[3]; QQnxIntegration *m_integration; - QQnxAbstractVirtualKeyboard &m_virtualKeyboad; + QQnxAbstractVirtualKeyboard &m_virtualKeyboard; }; -Q_DECLARE_METATYPE(extracted_text_t*) - QT_END_NAMESPACE #endif // QQNXINPUTCONTEXT_H diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp index f444d34b5e..9270f1ed6b 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp +++ b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -46,6 +46,7 @@ #include <QtCore/QDebug> #include <QtGui/QGuiApplication> +#include <QtGui/QInputMethodEvent> #if defined(QQNXINPUTCONTEXT_DEBUG) #define qInputContextDebug qDebug @@ -179,7 +180,11 @@ void QQnxInputContext::setFocusObject(QObject *object) if (m_inputPanelVisible) hideInputPanel(); } else { - m_virtualKeyboard.setInputHintsFromObject(object); + QInputMethodQueryEvent query(Qt::ImHints); + QCoreApplication::sendEvent(object, &query); + int inputHints = query.value(Qt::ImHints).toInt(); + + m_virtualKeyboard.setInputHints(inputHints); if (!m_inputPanelVisible) showInputPanel(); diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 52f836abbe..b39311353c 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved. +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxintegration.h" #if defined(QQNX_SCREENEVENTTHREAD) #include "qqnxscreeneventthread.h" @@ -123,6 +125,10 @@ static inline QQnxIntegration::Options parseOptions(const QStringList ¶mList options |= QQnxIntegration::FullScreenApplication; } + if (!paramList.contains(QLatin1String("flush-screen-context"))) { + options |= QQnxIntegration::AlwaysFlushScreenContext; + } + // On Blackberry the first window is treated as a root window #ifdef Q_OS_BLACKBERRY if (!paramList.contains(QLatin1String("no-rootwindow"))) { @@ -165,14 +171,12 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) #if !defined(QT_NO_DRAGANDDROP) , m_drag(new QSimpleDrag()) #endif - , m_options(parseOptions(paramList)) { + ms_options = parseOptions(paramList); qIntegrationDebug() << Q_FUNC_INFO; // Open connection to QNX composition manager - errno = 0; - int result = screen_create_context(&m_screenContext, SCREEN_APPLICATION_CONTEXT); - if (result != 0) - qFatal("QQnx: failed to connect to composition manager, errno=%d", errno); + Q_SCREEN_CRITICALERROR(screen_create_context(&ms_screenContext, SCREEN_APPLICATION_CONTEXT), + "Failed to create screen context"); // Not on BlackBerry, it has specialized event dispatcher which also handles navigator events #if !defined(Q_OS_BLACKBERRY) && defined(QQNX_PPS) @@ -191,7 +195,7 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) // Create/start event thread #if defined(QQNX_SCREENEVENTTHREAD) - m_screenEventThread = new QQnxScreenEventThread(m_screenContext, m_screenEventHandler); + m_screenEventThread = new QQnxScreenEventThread(ms_screenContext, m_screenEventHandler); m_screenEventThread->start(); #endif @@ -251,6 +255,9 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) #if defined(QQNX_PPS) // Set up the input context m_inputContext = new QQnxInputContext(this, *m_virtualKeyboard); +#if defined(QQNX_IMF) + m_screenEventHandler->addScreenEventFilter(m_inputContext); +#endif #endif } @@ -271,17 +278,6 @@ QQnxIntegration::~QQnxIntegration() delete m_drag; #endif -#if defined(QQNX_PPS) - // Destroy the hardware button notifier - delete m_buttonsNotifier; - - // Destroy input context - delete m_inputContext; -#endif - - // Destroy the keyboard class. - delete m_virtualKeyboard; - #if !defined(QT_NO_CLIPBOARD) // Delete the clipboard delete m_clipboard; @@ -314,13 +310,24 @@ QQnxIntegration::~QQnxIntegration() destroyDisplays(); // Close connection to QNX composition manager - screen_destroy_context(m_screenContext); + screen_destroy_context(ms_screenContext); #if !defined(QT_NO_OPENGL) // Cleanup global OpenGL resources QQnxGLContext::shutdown(); #endif +#if defined(QQNX_PPS) + // Destroy the hardware button notifier + delete m_buttonsNotifier; + + // Destroy input context + delete m_inputContext; +#endif + + // Destroy the keyboard class. + delete m_virtualKeyboard; + // Destroy services class delete m_services; @@ -355,10 +362,10 @@ QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const const bool needRootWindow = options() & RootWindow; switch (surfaceType) { case QSurface::RasterSurface: - return new QQnxRasterWindow(window, m_screenContext, needRootWindow); + return new QQnxRasterWindow(window, ms_screenContext, needRootWindow); #if !defined(QT_NO_OPENGL) case QSurface::OpenGLSurface: - return new QQnxEglWindow(window, m_screenContext, needRootWindow); + return new QQnxEglWindow(window, ms_screenContext, needRootWindow); #endif default: qFatal("QQnxWindow: unsupported window API"); @@ -441,7 +448,7 @@ QPlatformDrag *QQnxIntegration::drag() const QVariant QQnxIntegration::styleHint(QPlatformIntegration::StyleHint hint) const { qIntegrationDebug() << Q_FUNC_INFO; - if ((hint == ShowIsFullScreen) && (m_options & FullScreenApplication)) + if ((hint == ShowIsFullScreen) && (ms_options & FullScreenApplication)) return true; return QPlatformIntegration::styleHint(hint); @@ -495,11 +502,10 @@ void QQnxIntegration::createDisplays() { qIntegrationDebug() << Q_FUNC_INFO; // Query number of displays - errno = 0; - int displayCount; - int result = screen_get_context_property_iv(m_screenContext, SCREEN_PROPERTY_DISPLAY_COUNT, &displayCount); - if (result != 0) - qFatal("QQnxIntegration: failed to query display count, errno=%d", errno); + int displayCount = 0; + int result = screen_get_context_property_iv(ms_screenContext, SCREEN_PROPERTY_DISPLAY_COUNT, + &displayCount); + Q_SCREEN_CRITICALERROR(result, "Failed to query display count"); if (displayCount < 1) { // Never happens, even if there's no display, libscreen returns 1 @@ -507,23 +513,20 @@ void QQnxIntegration::createDisplays() } // Get all displays - errno = 0; screen_display_t *displays = (screen_display_t *)alloca(sizeof(screen_display_t) * displayCount); - result = screen_get_context_property_pv(m_screenContext, SCREEN_PROPERTY_DISPLAYS, (void **)displays); - if (result != 0) - qFatal("QQnxIntegration: failed to query displays, errno=%d", errno); + result = screen_get_context_property_pv(ms_screenContext, SCREEN_PROPERTY_DISPLAYS, + (void **)displays); + Q_SCREEN_CRITICALERROR(result, "Failed to query displays"); // If it's primary, we create a QScreen for it even if it's not attached // since Qt will dereference QGuiApplication::primaryScreen() createDisplay(displays[0], /*isPrimary=*/true); for (int i=1; i<displayCount; i++) { - int isAttached = 0; - result = screen_get_display_property_iv(displays[i], SCREEN_PROPERTY_ATTACHED, &isAttached); - if (result != 0) { - qWarning("QQnxIntegration: failed to query display attachment, errno=%d", errno); - isAttached = 1; // assume attached - } + int isAttached = 1; + result = screen_get_display_property_iv(displays[i], SCREEN_PROPERTY_ATTACHED, + &isAttached); + Q_SCREEN_CHECKERROR(result, "Failed to query display attachment"); if (!isAttached) { qIntegrationDebug() << Q_FUNC_INFO << "Skipping non-attached display" << i; @@ -537,7 +540,7 @@ void QQnxIntegration::createDisplays() void QQnxIntegration::createDisplay(screen_display_t display, bool isPrimary) { - QQnxScreen *screen = new QQnxScreen(m_screenContext, display, isPrimary); + QQnxScreen *screen = new QQnxScreen(ms_screenContext, display, isPrimary); m_screens.append(screen); screenAdded(screen); screen->adjustOrientation(); @@ -584,11 +587,20 @@ QQnxScreen *QQnxIntegration::primaryDisplay() const return m_screens.first(); } -QQnxIntegration::Options QQnxIntegration::options() const +QQnxIntegration::Options QQnxIntegration::options() +{ + return ms_options; +} + +screen_context_t QQnxIntegration::screenContext() { - return m_options; + return ms_screenContext; } +screen_context_t QQnxIntegration::ms_screenContext = 0; + +QQnxIntegration::Options QQnxIntegration::ms_options = 0; + bool QQnxIntegration::supportsNavigatorEvents() const { // If QQNX_PPS or Q_OS_BLACKBERRY is defined then we have navigator diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index 8b5614fe4f..b5f03d4727 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -85,7 +85,8 @@ public: enum Option { // Options to be passed on command line. NoOptions = 0x0, FullScreenApplication = 0x1, - RootWindow = 0x2 + RootWindow = 0x2, + AlwaysFlushScreenContext = 0x4 }; Q_DECLARE_FLAGS(Options, Option) explicit QQnxIntegration(const QStringList ¶mList); @@ -137,7 +138,8 @@ public: void createDisplay(screen_display_t display, bool isPrimary); void removeDisplay(QQnxScreen *screen); QQnxScreen *primaryDisplay() const; - Options options() const; + static Options options(); + static screen_context_t screenContext(); private: void createDisplays(); @@ -146,7 +148,7 @@ private: static void addWindow(screen_window_t qnxWindow, QWindow *window); static void removeWindow(screen_window_t qnxWindow); - screen_context_t m_screenContext; + static screen_context_t ms_screenContext; #if defined(QQNX_SCREENEVENTTHREAD) QQnxScreenEventThread *m_screenEventThread; #endif @@ -176,7 +178,7 @@ private: static QQnxWindowMapper ms_windowMapper; static QMutex ms_windowMapperMutex; - const Options m_options; + static Options ms_options; friend class QQnxWindow; }; diff --git a/src/plugins/platforms/qnx/qqnxnativeinterface.cpp b/src/plugins/platforms/qnx/qqnxnativeinterface.cpp index e468b051cd..df9d96739a 100644 --- a/src/plugins/platforms/qnx/qqnxnativeinterface.cpp +++ b/src/plugins/platforms/qnx/qqnxnativeinterface.cpp @@ -44,6 +44,9 @@ #include "qqnxglcontext.h" #include "qqnxscreen.h" #include "qqnxwindow.h" +#if defined(QQNX_IMF) +#include "qqnxinputcontext_imf.h" +#endif #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> @@ -85,10 +88,27 @@ void *QQnxNativeInterface::nativeResourceForContext(const QByteArray &resource, void QQnxNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) { + QQnxWindow *qnxWindow = static_cast<QQnxWindow*>(window); + if (name == QStringLiteral("mmRendererWindowName")) { - QQnxWindow *qnxWindow = static_cast<QQnxWindow*>(window); qnxWindow->setMMRendererWindowName(value.toString()); + } else if (name == QStringLiteral("windowGroup")) { + if (value.isNull()) + qnxWindow->joinWindowGroup(QByteArray()); + else if (value.canConvert<QByteArray>()) + qnxWindow->joinWindowGroup(value.toByteArray()); } } +QPlatformNativeInterface::NativeResourceForIntegrationFunction QQnxNativeInterface::nativeResourceFunctionForIntegration(const QByteArray &resource) +{ +#if defined(QQNX_IMF) + if (resource == "blackberryIMFSetHighlightColor") + return reinterpret_cast<NativeResourceForIntegrationFunction>(QQnxInputContext::setHighlightColor); + if (resource == "blackberryIMFCheckSpelling") + return reinterpret_cast<NativeResourceForIntegrationFunction>(QQnxInputContext::checkSpelling); +#endif + return 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxnativeinterface.h b/src/plugins/platforms/qnx/qqnxnativeinterface.h index dfd386214e..e2fdd32689 100644 --- a/src/plugins/platforms/qnx/qqnxnativeinterface.h +++ b/src/plugins/platforms/qnx/qqnxnativeinterface.h @@ -51,8 +51,10 @@ class QQnxNativeInterface : public QPlatformNativeInterface public: void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen); + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context); void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); + NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp index 0d8daac0ee..2c0639e8e3 100644 --- a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxrasterwindow.h" #include "qqnxscreen.h" @@ -108,10 +110,9 @@ void QQnxRasterWindow::post(const QRegion &dirty) 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(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0); - if (result != 0) - qFatal("QQnxWindow: failed to post window buffer, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0), + "Failed to post window"); // Advance to next nender buffer m_previousBufferIndex = m_currentBufferIndex++; @@ -141,28 +142,23 @@ QQnxBuffer &QQnxRasterWindow::renderBuffer() // Check if render buffer is invalid if (m_currentBufferIndex == -1) { // Get all buffers available for rendering - errno = 0; screen_buffer_t buffers[MAX_BUFFER_COUNT]; - int result = screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_RENDER_BUFFERS, (void **)buffers); - if (result != 0) - qFatal("QQnxRasterWindow: failed to query window buffers, errno=%d", errno); + const int result = screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_RENDER_BUFFERS, + (void **)buffers); + Q_SCREEN_CRITICALERROR(result, "Failed to query window buffers"); // Wrap each buffer and clear for (int i = 0; i < MAX_BUFFER_COUNT; ++i) { m_buffers[i] = QQnxBuffer(buffers[i]); // Clear Buffer - errno = 0; int bg[] = { SCREEN_BLIT_COLOR, 0x00000000, SCREEN_BLIT_END }; - result = screen_fill(screen()->nativeContext(), buffers[i], bg); - if (result != 0) - qFatal("QQnxWindow: failed to clear window buffer, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_fill(screen()->nativeContext(), buffers[i], bg), + "Failed to clear window buffer"); } - errno = 0; - result = screen_flush_blits(screen()->nativeContext(), 0); - if (result != 0) - qFatal("QQnxWindow: failed to flush blits, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_flush_blits(screen()->nativeContext(), 0), + "Failed to flush blits"); // Use the first available render buffer m_currentBufferIndex = 0; @@ -172,11 +168,17 @@ QQnxBuffer &QQnxRasterWindow::renderBuffer() return m_buffers[m_currentBufferIndex]; } +void QQnxRasterWindow::setParent(const QPlatformWindow *wnd) +{ + QQnxWindow::setParent(wnd); + adjustBufferSize(); +} + void QQnxRasterWindow::adjustBufferSize() { // When having a raster window we don't need any buffers, since // Qt will draw to the parent TLW backing store. - const QSize windowSize = m_parentWindow ? QSize(1,1) : window()->size(); + const QSize windowSize = window()->parent() ? QSize(1,1) : window()->size(); if (windowSize != bufferSize()) setBufferSize(windowSize); } @@ -226,20 +228,16 @@ void QQnxRasterWindow::blitPreviousToCurrent(const QRegion ®ion, int dx, int SCREEN_BLIT_END }; // Queue blit operation - errno = 0; - const int result = screen_blit(m_screenContext, currentBuffer.nativeBuffer(), - previousBuffer.nativeBuffer(), attribs); - if (result != 0) - qFatal("QQnxWindow: failed to blit buffers, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_blit(m_screenContext, currentBuffer.nativeBuffer(), + previousBuffer.nativeBuffer(), attribs), + "Failed to blit buffers"); } // Check if flush requested if (flush) { // Wait for all blits to complete - errno = 0; - const int result = screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE); - if (result != 0) - qFatal("QQnxWindow: failed to flush blits, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE), + "Failed to flush blits"); // Buffer was modified outside the CPU currentBuffer.invalidateInCache(); diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.h b/src/plugins/platforms/qnx/qqnxrasterwindow.h index ad34b3ccf2..2be5f63464 100644 --- a/src/plugins/platforms/qnx/qqnxrasterwindow.h +++ b/src/plugins/platforms/qnx/qqnxrasterwindow.h @@ -60,6 +60,8 @@ public: bool hasBuffers() const { return !bufferSize().isEmpty(); } + void setParent(const QPlatformWindow *window); + void adjustBufferSize(); protected: diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index 3a0607f214..a6c69164c7 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxscreen.h" #include "qqnxwindow.h" #include "qqnxcursor.h" @@ -75,10 +77,9 @@ QT_BEGIN_NAMESPACE static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { int val[2]; - errno = 0; const int result = screen_get_display_property_iv(display, SCREEN_PROPERTY_PHYSICAL_SIZE, val); + Q_SCREEN_CHECKERROR(result, "Failed to query display physical size"); if (result != 0) { - qFatal("QQnxScreen: failed to query display physical size, errno=%d", errno); return QSize(150, 90); } @@ -163,19 +164,16 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, { qScreenDebug() << Q_FUNC_INFO; // Cache initial orientation of this display - errno = 0; - int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, &m_initialRotation); - if (result != 0) - qFatal("QQnxScreen: failed to query display rotation, errno=%d", errno); + int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, + &m_initialRotation); + Q_SCREEN_CHECKERROR(result, "Failed to query display rotation"); m_currentRotation = m_initialRotation; // Cache size of this display in pixels - errno = 0; int val[2]; - result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val); - if (result != 0) - qFatal("QQnxScreen: failed to query display size, errno=%d", errno); + Q_SCREEN_CRITICALERROR(screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val), + "Failed to query display size"); m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); @@ -200,6 +198,9 @@ QQnxScreen::~QQnxScreen() Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->setScreen(0); + if (m_coverWindow) + m_coverWindow->setScreen(0); + delete m_cursor; } @@ -505,7 +506,6 @@ void QQnxScreen::raiseWindow(QQnxWindow *window) if (window != m_coverWindow) { removeWindow(window); m_childWindows.push_back(window); - updateHierarchy(); } } @@ -516,7 +516,6 @@ void QQnxScreen::lowerWindow(QQnxWindow *window) if (window != m_coverWindow) { removeWindow(window); m_childWindows.push_front(window); - updateHierarchy(); } } @@ -671,7 +670,7 @@ void QQnxScreen::newWindowCreated(void *window) // Otherwise, assume that if a foreign window already has a Z-Order both negative and // less than the default Z-Order installed by mmrender on windows it creates, // the windows should be treated as an underlay. Otherwise, we treat it as an overlay. - if (!windowName.isEmpty() && windowName.startsWith("BbVideoWindowControl")) { + if (!windowName.isEmpty() && windowName.startsWith("MmRendererVideoWindowControl")) { addMultimediaWindow(windowName, windowHandle); } else if (!findWindow(windowHandle)) { if (zorder <= MAX_UNDERLAY_ZORDER) @@ -728,8 +727,6 @@ void QQnxScreen::activateWindowGroup(const QByteArray &id) if (m_coverWindow) m_coverWindow->setExposed(false); - - QWindowSystemInterface::handleWindowActivated(window); } void QQnxScreen::deactivateWindowGroup(const QByteArray &id) @@ -744,8 +741,6 @@ void QQnxScreen::deactivateWindowGroup(const QByteArray &id) Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->setExposed(false); - - QWindowSystemInterface::handleWindowActivated(rootWindow()->window()); } QQnxWindow *QQnxScreen::rootWindow() const diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index 61c47e6c72..d39a210d4b 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -80,7 +80,7 @@ public: int nativeFormat() const { return (depth() == 32) ? SCREEN_FORMAT_RGBA8888 : SCREEN_FORMAT_RGB565; } screen_display_t nativeDisplay() const { return m_display; } screen_context_t nativeContext() const { return m_screenContext; } - const char *windowGroupName() const { return rootWindow()->groupName().constData(); } + const char *windowGroupName() const { return m_rootWindow ? m_rootWindow->groupName().constData() : 0; } QQnxWindow *findWindow(screen_window_t windowHandle); diff --git a/src/plugins/platforms/qnx/qqnxscreeneventfilter.h b/src/plugins/platforms/qnx/qqnxscreeneventfilter.h new file mode 100644 index 0000000000..f9ecadd2a9 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxscreeneventfilter.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXSCREENEVENTFILTER_H +#define QQNXSCREENEVENTFILTER_H + +QT_BEGIN_NAMESPACE + +class QQnxScreenEventFilter +{ +protected: + ~QQnxScreenEventFilter() {} + +public: + virtual bool handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId) = 0; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 6f06797393..8757e391ef 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxscreeneventhandler.h" #if defined(QQNX_SCREENEVENTTHREAD) #include "qqnxscreeneventthread.h" @@ -46,6 +48,7 @@ #include "qqnxintegration.h" #include "qqnxkeytranslator.h" #include "qqnxscreen.h" +#include "qqnxscreeneventfilter.h" #include <QDebug> #include <QGuiApplication> @@ -90,14 +93,22 @@ QQnxScreenEventHandler::QQnxScreenEventHandler(QQnxIntegration *integration) } } +void QQnxScreenEventHandler::addScreenEventFilter(QQnxScreenEventFilter *filter) +{ + m_eventFilters.append(filter); +} + +void QQnxScreenEventHandler::removeScreenEventFilter(QQnxScreenEventFilter *filter) +{ + m_eventFilters.removeOne(filter); +} + bool QQnxScreenEventHandler::handleEvent(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("QQNX: failed to query event type, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType), + "Failed to query event type"); return handleEvent(event, qnxType); } @@ -228,35 +239,44 @@ void QQnxScreenEventHandler::processEventsFromScreenThread() void QQnxScreenEventHandler::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("QQNX: failed to query event flags, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_FLAGS, &flags), + "Failed to query event flags"); // get key code - errno = 0; int sym; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SYM, &sym); - if (result) - qFatal("QQNX: failed to query event sym, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SYM, &sym), + "Failed to query event sym"); int modifiers; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_MODIFIERS, &modifiers); - if (result) - qFatal("QQNX: failed to query event modifiers, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_MODIFIERS, &modifiers), + "Failed to query event modifieres"); int scan; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SCAN, &scan); - if (result) - qFatal("QQNX: failed to query event modifiers, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SCAN, &scan), + "Failed to query event scan"); int cap; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap); - if (result) - qFatal("QQNX: failed to query event cap, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap), + "Failed to query event cap"); + + int sequenceId = 0; +#if defined(Q_OS_BLACKBERRY) + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_SEQUENCE_ID, &sequenceId), + "Failed to query event seqId"); +#endif - injectKeyboardEvent(flags, sym, modifiers, scan, cap); + bool inject = true; + Q_FOREACH (QQnxScreenEventFilter *filter, m_eventFilters) { + if (filter->handleKeyboardEvent(flags, sym, modifiers, scan, cap, sequenceId)) { + inject = false; + break; + } + } + + if (inject) + injectKeyboardEvent(flags, sym, modifiers, scan, cap); } void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event) @@ -266,35 +286,32 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event) // Query the window that was clicked screen_window_t qnxWindow; void *handle; - int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); - if (result) - qFatal("QQNX: failed to query event window, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle), + "Failed to query event window"); qnxWindow = static_cast<screen_window_t>(handle); // Query the button states int buttonState = 0; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttonState); - if (result) - qFatal("QQNX: failed to query event button state, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttonState), + "Failed to query event button state"); // Query the window position int windowPos[2]; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos); - if (result) - qFatal("QQNX: failed to query event window position, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos), + "Failed to query event window position"); // Query the screen position int pos[2]; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos); - if (result) - qFatal("QQNX: failed to query event position, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos), + "Failed to query event position"); // Query the wheel delta int wheelDelta = 0; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheelDelta); - if (result) - qFatal("QQNX: failed to query event wheel delta, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheelDelta), + "Failed to query event wheel delta"); // Map window handle to top-level QWindow QWindow *w = QQnxIntegration::window(qnxWindow); @@ -314,10 +331,6 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event) } } - // If we don't have a navigator, we don't get activation events. - if (buttonState && w && w != QGuiApplication::focusWindow() && !m_qnxIntegration->supportsNavigatorEvents()) - QWindowSystemInterface::handleWindowActivated(w); - m_lastMouseWindow = qnxWindow; // Apply scaling to wheel delta and invert value for Qt. We'll probably want to scale @@ -376,34 +389,36 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event) void QQnxScreenEventHandler::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("QQNX: failed to query event position, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos), + "Failed to query event position"); QCursor::setPos(pos[0], pos[1]); // 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("QQNX: failed to query event window position, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos), + "Failed to query event window position"); // determine which finger touched - errno = 0; int touchId; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touchId); - if (result) - qFatal("QQNX: failed to query event touch id, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touchId), + "Failed to query event touch id"); // determine which window was touched - errno = 0; void *handle; - result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); - if (result) - qFatal("QQNX: failed to query event window, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle), + "Failed to query event window"); + + errno = 0; + int touchArea[2]; + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_SIZE, touchArea), + "Failed to query event touch area"); + + int touchPressure; + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_PRESSURE, &touchPressure), + "Failed to query event touch pressure"); screen_window_t qnxWindow = static_cast<screen_window_t>(handle); @@ -439,14 +454,23 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) QPointF(static_cast<qreal>(pos[0]) / screenSize.width(), static_cast<qreal>(pos[1]) / screenSize.height()); - m_touchPoints[touchId].area = QRectF(w->geometry().left() + windowPos[0], - w->geometry().top() + windowPos[1], 0.0, 0.0); + m_touchPoints[touchId].area = QRectF(w->geometry().left() + windowPos[0] - (touchArea[0]>>1), + w->geometry().top() + windowPos[1] - (touchArea[1]>>1), + 0.0, 0.0); QWindow *parent = w->parent(); while (parent) { m_touchPoints[touchId].area.translate(parent->geometry().topLeft()); parent = parent->parent(); } + //Qt expects the pressure between 0 and 1. There is however no definit upper limit for + //the integer value of touch event pressure. The 200 was determined by experiment, it + //usually does not get higher than that. + m_touchPoints[touchId].pressure = static_cast<qreal>(touchPressure)/200.0; + // Can happen, because there is no upper limit for pressure + if (m_touchPoints[touchId].pressure > 1) + m_touchPoints[touchId].pressure = 1; + // determine event type and update state of current touch point QEvent::Type type = QEvent::None; switch (qnxType) { @@ -489,8 +513,9 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) void QQnxScreenEventHandler::handleCloseEvent(screen_event_t event) { screen_window_t window = 0; - if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) - qFatal("QQnx: failed to query window property, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window), + "Failed to query window property"); Q_EMIT windowClosed(window); @@ -503,8 +528,9 @@ void QQnxScreenEventHandler::handleCloseEvent(screen_event_t event) void QQnxScreenEventHandler::handleCreateEvent(screen_event_t event) { screen_window_t window = 0; - if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) - qFatal("QQnx: failed to query window property, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window), + "Failed to query window property"); Q_EMIT newWindowCreated(window); } @@ -556,8 +582,9 @@ void QQnxScreenEventHandler::handlePropertyEvent(screen_event_t event) { errno = 0; int objectType; - if (screen_get_event_property_iv(event, SCREEN_PROPERTY_OBJECT_TYPE, &objectType) != 0) - qFatal("QQNX: failed to query object type property, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_get_event_property_iv(event, SCREEN_PROPERTY_OBJECT_TYPE, &objectType), + "Failed to query object type property"); if (objectType != SCREEN_OBJECT_TYPE_WINDOW) return; @@ -589,9 +616,15 @@ void QQnxScreenEventHandler::handleKeyboardFocusPropertyEvent(screen_window_t wi if (window && screen_get_window_property_iv(window, SCREEN_PROPERTY_KEYBOARD_FOCUS, &focus) != 0) qFatal("QQnx: failed to query keyboard focus property, errno=%d", errno); - QWindow *w = focus ? QQnxIntegration::window(window) : 0; - - QWindowSystemInterface::handleWindowActivated(w); + QWindow *focusWindow = QQnxIntegration::window(window); + if (focus) { + QWindowSystemInterface::handleWindowActivated(focusWindow); + } else if (focusWindow == QGuiApplication::focusWindow()) { + // Deactivate only if the window was the focus window. + // Screen might send a keyboard focus event for a newly created + // window on the secondary screen, with focus 0. + QWindowSystemInterface::handleWindowActivated(0); + } } #include "moc_qqnxscreeneventhandler.cpp" diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h index 1fdb2c83cd..a7bcd449ee 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE class QQnxIntegration; +class QQnxScreenEventFilter; #if defined(QQNX_SCREENEVENTTHREAD) class QQnxScreenEventThread; #endif @@ -59,6 +60,9 @@ class QQnxScreenEventHandler : public QObject public: explicit QQnxScreenEventHandler(QQnxIntegration *integration); + void addScreenEventFilter(QQnxScreenEventFilter *filter); + void removeScreenEventFilter(QQnxScreenEventFilter *filter); + bool handleEvent(screen_event_t event); bool handleEvent(screen_event_t event, int qnxType); @@ -99,6 +103,7 @@ private: screen_window_t m_lastMouseWindow; QTouchDevice *m_touchDevice; QWindowSystemInterface::TouchPoint m_touchPoints[MaximumTouchPoints]; + QList<QQnxScreenEventFilter*> m_eventFilters; #if defined(QQNX_SCREENEVENTTHREAD) QQnxScreenEventThread *m_eventThread; #endif diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp index 25a597bab9..156ba8a780 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxscreeneventthread.h" #include "qqnxscreeneventhandler.h" @@ -92,30 +94,34 @@ void QQnxScreenEventThread::run() { qScreenEventThreadDebug() << Q_FUNC_INFO << "screen event thread started"; + int errorCounter = 0; // loop indefinitely while (!m_quit) { screen_event_t event; // create screen event - errno = 0; - int result = screen_create_event(&event); - if (result) - qFatal("QQNX: failed to create screen event, errno=%d", errno); - + Q_SCREEN_CHECKERROR(screen_create_event(&event), "Failed to create screen event"); // block until screen event is available - errno = 0; - result = screen_get_event(m_screenContext, event, -1); - if (result) - qFatal("QQNX: failed to get screen event, errno=%d", errno); + const int result = screen_get_event(m_screenContext, event, -1); + Q_SCREEN_CRITICALERROR(result, "Failed to get screen event"); + // Only allow 50 consecutive errors before we exit the thread + if (!result) { + errorCounter++; + if (errorCounter > 50) + m_quit = true; + + screen_destroy_event(event); + continue; + } else { + errorCounter = 0; + } // process received event // get the event type - errno = 0; int qnxType; - result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType); - if (result) - qFatal("QQNX: failed to query screen event type, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType), + "Failed to query screen event type"); if (qnxType == SCREEN_EVENT_USER) { // treat all user events as shutdown requests @@ -145,25 +151,19 @@ void QQnxScreenEventThread::shutdown() screen_event_t event; // create screen event - errno = 0; - int result = screen_create_event(&event); - if (result) - qFatal("QQNX: failed to create screen event, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_create_event(&event), + "Failed to create screen event"); // 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("QQNX: failed to set screen event type, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_event_property_iv(event, SCREEN_PROPERTY_TYPE, &type), + "Failed to set screen type"); // NOTE: ignore SCREEN_PROPERTY_USER_DATA; treat all user events as shutdown events // post event to event loop so it will wake up and die - errno = 0; - result = screen_send_event(m_screenContext, event, getpid()); - if (result) - qFatal("QQNX: failed to set screen event type, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_send_event(m_screenContext, event, getpid()), + "Failed to set screen event type"); // cleanup screen_destroy_event(event); diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp index 11eb4a5082..08de94a082 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2012 Research In Motion +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -46,6 +46,9 @@ #include <bps/event.h> #include <bps/locale.h> #include <bps/virtualkeyboard.h> +#if defined(Q_OS_BLACKBERRY) +#include <bbndk.h> +#endif #if defined(QQNXVIRTUALKEYBOARD_DEBUG) #define qVirtualKeyboardDebug qDebug @@ -89,7 +92,7 @@ bool QQnxVirtualKeyboardBps::showKeyboard() // They keyboard's mode is global between applications, we have to set it each time if ( !isVisible() ) - applyKeyboardMode(keyboardMode()); + applyKeyboardOptions(); virtualkeyboard_show(); return true; @@ -102,48 +105,76 @@ bool QQnxVirtualKeyboardBps::hideKeyboard() return true; } -void QQnxVirtualKeyboardBps::applyKeyboardMode(KeyboardMode mode) +void QQnxVirtualKeyboardBps::applyKeyboardOptions() { - virtualkeyboard_layout_t layout = VIRTUALKEYBOARD_LAYOUT_DEFAULT; + virtualkeyboard_layout_t layout = keyboardLayout(); + virtualkeyboard_enter_t enter = enterKey(); - switch (mode) { - case Url: - layout = VIRTUALKEYBOARD_LAYOUT_URL; - break; + qVirtualKeyboardDebug() << Q_FUNC_INFO << "mode=" << keyboardMode() << "enterKey=" << enterKeyType(); - case Email: - layout = VIRTUALKEYBOARD_LAYOUT_EMAIL; - break; + virtualkeyboard_change_options(layout, enter); +} +virtualkeyboard_layout_t QQnxVirtualKeyboardBps::keyboardLayout() const +{ + switch (keyboardMode()) { + case Url: + return VIRTUALKEYBOARD_LAYOUT_URL; + case Email: + return VIRTUALKEYBOARD_LAYOUT_EMAIL; case Web: - layout = VIRTUALKEYBOARD_LAYOUT_WEB; - break; - + return VIRTUALKEYBOARD_LAYOUT_WEB; case NumPunc: - layout = VIRTUALKEYBOARD_LAYOUT_NUM_PUNC; - break; - + return VIRTUALKEYBOARD_LAYOUT_NUM_PUNC; + case Number: + return VIRTUALKEYBOARD_LAYOUT_NUMBER; case Symbol: - layout = VIRTUALKEYBOARD_LAYOUT_SYMBOL; - break; - + return VIRTUALKEYBOARD_LAYOUT_SYMBOL; case Phone: - layout = VIRTUALKEYBOARD_LAYOUT_PHONE; - break; - + return VIRTUALKEYBOARD_LAYOUT_PHONE; case Pin: - layout = VIRTUALKEYBOARD_LAYOUT_PIN; - break; - + return VIRTUALKEYBOARD_LAYOUT_PIN; + case Password: + return VIRTUALKEYBOARD_LAYOUT_PASSWORD; +#if defined(Q_OS_BLACKBERRY) +#if BBNDK_VERSION_AT_LEAST(10, 2, 1) + case Alphanumeric: + return VIRTUALKEYBOARD_LAYOUT_ALPHANUMERIC; +#endif +#endif case Default: // fall through default: - layout = VIRTUALKEYBOARD_LAYOUT_DEFAULT; - break; + return VIRTUALKEYBOARD_LAYOUT_DEFAULT; } - qVirtualKeyboardDebug() << Q_FUNC_INFO << "mode=" << mode; + return VIRTUALKEYBOARD_LAYOUT_DEFAULT; +} + +virtualkeyboard_enter_t QQnxVirtualKeyboardBps::enterKey() const +{ + switch (enterKeyType()) { + case Connect: + return VIRTUALKEYBOARD_ENTER_CONNECT; + case Done: + return VIRTUALKEYBOARD_ENTER_DONE; + case Go: + return VIRTUALKEYBOARD_ENTER_GO; + case Join: + return VIRTUALKEYBOARD_ENTER_JOIN; + case Next: + return VIRTUALKEYBOARD_ENTER_NEXT; + case Search: + return VIRTUALKEYBOARD_ENTER_SEARCH; + case Send: + return VIRTUALKEYBOARD_ENTER_SEND; + case Submit: + return VIRTUALKEYBOARD_ENTER_SUBMIT; + case Default: // fall through + default: + return VIRTUALKEYBOARD_ENTER_DEFAULT; + } - virtualkeyboard_change_options(layout, VIRTUALKEYBOARD_ENTER_DEFAULT); + return VIRTUALKEYBOARD_ENTER_DEFAULT; } bool QQnxVirtualKeyboardBps::handleLocaleEvent(bps_event_t *event) diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.h b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.h index 43ecb4ecf8..5749deb4e0 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.h +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardbps.h @@ -43,6 +43,7 @@ #define QQNXVIRTUALKEYBOARDBPS_H #include "qqnxabstractvirtualkeyboard.h" +#include <bps/virtualkeyboard.h> struct bps_event_t; @@ -60,11 +61,14 @@ public: bool hideKeyboard(); protected: - void applyKeyboardMode(KeyboardMode mode); + void applyKeyboardOptions(); private: bool handleLocaleEvent(bps_event_t *event); bool handleVirtualKeyboardEvent(bps_event_t *event); + + virtualkeyboard_layout_t keyboardLayout() const; + virtualkeyboard_enter_t enterKey() const; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp index 20fce3da70..2b6ee3d1dc 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp @@ -67,9 +67,6 @@ QT_BEGIN_NAMESPACE const char *QQnxVirtualKeyboardPps::ms_PPSPath = "/pps/services/input/control"; const size_t QQnxVirtualKeyboardPps::ms_bufferSize = 2048; -// Huge hack for keyboard shadow (see QNX PR 88400). Should be removed ASAP. -#define KEYBOARD_SHADOW_HEIGHT 8 - QQnxVirtualKeyboardPps::QQnxVirtualKeyboardPps() : m_encoder(0), m_decoder(0), @@ -91,11 +88,6 @@ void QQnxVirtualKeyboardPps::start() return; } -void QQnxVirtualKeyboardPps::applyKeyboardMode(KeyboardMode mode) -{ - applyKeyboardModeOptions(mode); -} - void QQnxVirtualKeyboardPps::close() { delete m_readNotifier; @@ -159,18 +151,14 @@ bool QQnxVirtualKeyboardPps::connect() bool QQnxVirtualKeyboardPps::queryPPSInfo() { + if (!prepareToSend()) + return false; + // Request info, requires id to regenerate res message. pps_encoder_add_string(m_encoder, "msg", "info"); pps_encoder_add_string(m_encoder, "id", "libWebView"); - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { - close(); - return false; - } - - pps_encoder_reset(m_encoder); - - return true; + return writeCurrentPPSEncoder(); } void QQnxVirtualKeyboardPps::ppsDataReady() @@ -257,9 +245,6 @@ void QQnxVirtualKeyboardPps::handleKeyboardInfoMessage() } const QString countryId = QString::fromLatin1(value); - // HUGE hack, should be removed ASAP. - newHeight -= KEYBOARD_SHADOW_HEIGHT; // We want to ignore the 8 pixel shadow above the keyboard. (PR 88400) - setHeight(newHeight); const QLocale locale = QLocale(languageId + QLatin1Char('_') + countryId); @@ -272,13 +257,12 @@ bool QQnxVirtualKeyboardPps::showKeyboard() { qVirtualKeyboardDebug() << Q_FUNC_INFO; - // Try to connect. - if (m_fd == -1 && !connect()) + if (!prepareToSend()) 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(keyboardMode()); + applyKeyboardOptions(); if (isVisible()) return true; @@ -288,140 +272,110 @@ bool QQnxVirtualKeyboardPps::showKeyboard() // Send the show message. pps_encoder_add_string(m_encoder, "msg", "show"); - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { - close(); - return false; - } - - pps_encoder_reset(m_encoder); - - // Return true if no error occurs. Sizing response will be triggered when confirmation of - // the change arrives. - return true; + return writeCurrentPPSEncoder(); } bool QQnxVirtualKeyboardPps::hideKeyboard() { qVirtualKeyboardDebug() << Q_FUNC_INFO; - if (m_fd == -1 && !connect()) + if (!prepareToSend()) return false; pps_encoder_add_string(m_encoder, "msg", "hide"); - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { - close(); + return writeCurrentPPSEncoder(); +} - //Try again. - if (connect()) { - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { - close(); - return false; - } - } - else - return false; - } +bool QQnxVirtualKeyboardPps::prepareToSend() +{ + if (m_fd == -1 && !connect()) + return false; pps_encoder_reset(m_encoder); + return true; +} - // Return true if no error occurs. Sizing response will be triggered when confirmation of - // the change arrives. +bool QQnxVirtualKeyboardPps::writeCurrentPPSEncoder() +{ + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + close(); + return false; + } return true; } -void QQnxVirtualKeyboardPps::applyKeyboardModeOptions(KeyboardMode mode) +void QQnxVirtualKeyboardPps::applyKeyboardOptions() { - // Try to connect. - if (m_fd == -1 && !connect()) + if (!prepareToSend()) return; // Send the options message. pps_encoder_add_string(m_encoder, "msg", "options"); - pps_encoder_start_object(m_encoder, "dat"); - switch (mode) { + + pps_encoder_add_string(m_encoder, "enter", enterKeyTypeStr()); + pps_encoder_add_string(m_encoder, "type", keyboardModeStr()); + + pps_encoder_end_object(m_encoder); + + writeCurrentPPSEncoder(); +} + +const char* QQnxVirtualKeyboardPps::keyboardModeStr() const +{ + switch (keyboardMode()) { case Url: - addUrlModeOptions(); - break; + return "url"; case Email: - addEmailModeOptions(); - break; + return "email"; case Web: - addWebModeOptions(); - break; + return "web"; case NumPunc: - addNumPuncModeOptions(); - break; + return "num_punc"; + case Number: + return "number"; case Symbol: - addSymbolModeOptions(); - break; + return "symbol"; case Phone: - addPhoneModeOptions(); - break; + return "phone"; case Pin: - addPinModeOptions(); - break; + return "pin"; + case Password: + return "password"; + case Alphanumeric: + return "alphanumeric"; case Default: - default: - addDefaultModeOptions(); - break; + return "default"; } - pps_encoder_end_object(m_encoder); - - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) - close(); - - pps_encoder_reset(m_encoder); -} - -void QQnxVirtualKeyboardPps::addDefaultModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "default"); + return ""; } -void QQnxVirtualKeyboardPps::addUrlModeOptions() +const char* QQnxVirtualKeyboardPps::enterKeyTypeStr() const { - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "url"); -} - -void QQnxVirtualKeyboardPps::addEmailModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "email"); -} - -void QQnxVirtualKeyboardPps::addWebModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "web"); -} - -void QQnxVirtualKeyboardPps::addNumPuncModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "numPunc"); -} - -void QQnxVirtualKeyboardPps::addPhoneModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "phone"); -} - -void QQnxVirtualKeyboardPps::addPinModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "pin"); -} + switch (enterKeyType()) { + case DefaultReturn: + return "enter.default"; + case Connect: + return "enter.connect"; + case Done: + return "enter.done"; + case Go: + return "enter.go"; + case Join: + return "enter.join"; + case Next: + return "enter.next"; + case Search: + return "enter.search"; + case Send: + return "enter.send"; + case Submit: + return "enter.submit"; + } -void QQnxVirtualKeyboardPps::addSymbolModeOptions() -{ - pps_encoder_add_string(m_encoder, "enter", "enter.default"); - pps_encoder_add_string(m_encoder, "type", "symbol"); + return ""; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h index 6048868b08..2b56d5afbe 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE + class QSocketNotifier; class QQnxVirtualKeyboardPps : public QQnxAbstractVirtualKeyboard @@ -64,7 +65,7 @@ public Q_SLOTS: void start(); protected: - void applyKeyboardMode(KeyboardMode mode); + void applyKeyboardOptions(); private Q_SLOTS: void ppsDataReady(); @@ -76,15 +77,11 @@ private: bool queryPPSInfo(); void handleKeyboardInfoMessage(); - void applyKeyboardModeOptions(KeyboardMode mode); - void addDefaultModeOptions(); - void addUrlModeOptions(); - void addEmailModeOptions(); - void addWebModeOptions(); - void addNumPuncModeOptions(); - void addSymbolModeOptions(); - void addPhoneModeOptions(); - void addPinModeOptions(); + const char* keyboardModeStr() const; + const char* enterKeyTypeStr() const; + + bool prepareToSend(); + bool writeCurrentPPSEncoder(); pps_encoder_t *m_encoder; pps_decoder_t *m_decoder; diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 99071cf4f2..bb76efea64 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qqnxglobal.h" + #include "qqnxwindow.h" #include "qqnxintegration.h" #include "qqnxscreen.h" @@ -73,36 +75,45 @@ QT_BEGIN_NAMESPACE QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow) : QPlatformWindow(window), m_screenContext(context), - m_parentWindow(0), m_window(0), m_screen(0), + m_parentWindow(0), m_visible(false), m_exposed(true), m_windowState(Qt::WindowNoState), m_mmRendererWindow(0) { qWindowDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size(); - int result; QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window->screen()->handle()); - m_isTopLevel = ( needRootWindow && !platformScreen->rootWindow()) - || (!needRootWindow && !parent()) - || window->type() == Qt::CoverWindow; + if (window->type() == Qt::CoverWindow) { + // Cover windows have to be top level to be accessible to window delegate (i.e. navigator) + m_isTopLevel = true; + } else if (parent() || (window->type() & Qt::Dialog) == Qt::Dialog) { + // If we have a parent we are a child window. Sometimes we have to be a child even if we + // don't have a parent e.g. our parent might be in a different process. + m_isTopLevel = false; + } else { + // We're parentless. If we're not using a root window, we'll always be a top-level window + // otherwise only the first window is. + m_isTopLevel = !needRootWindow || !platformScreen->rootWindow(); + } - errno = 0; if (m_isTopLevel) { - result = screen_create_window(&m_window, m_screenContext); // Creates an application window + Q_SCREEN_CRITICALERROR(screen_create_window(&m_window, m_screenContext), + "Could not create top level window"); // Creates an application window if (window->type() != Qt::CoverWindow) { if (needRootWindow) platformScreen->setRootWindow(this); - createWindowGroup(); } } else { - result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + Q_SCREEN_CHECKERROR( + screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW), + "Could not create child window"); } - if (result != 0) - qFatal("QQnxWindow: failed to create window, errno=%d", errno); + + createWindowGroup(); } QQnxWindow::~QQnxWindow() @@ -130,7 +141,7 @@ void QQnxWindow::setGeometry(const QRect &rect) if (screen()->rootWindow() == this) //If this is the root window, it has to be shown fullscreen newGeometry = screen()->geometry(); - const QRect oldGeometry = setGeometryHelper(newGeometry); + setGeometryHelper(newGeometry); // Send a geometry change event to Qt (triggers resizeEvent() in QWindow/QWidget). @@ -140,71 +151,34 @@ void QQnxWindow::setGeometry(const QRect &rect) QWindowSystemInterface::handleGeometryChange(window(), newGeometry); QWindowSystemInterface::handleExposeEvent(window(), newGeometry); QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); - - // Now move all children. - if (!oldGeometry.isEmpty()) { - const QPoint offset = newGeometry.topLeft() - oldGeometry.topLeft(); - Q_FOREACH (QQnxWindow *childWindow, m_childWindows) - childWindow->setOffset(offset); - } } -QRect QQnxWindow::setGeometryHelper(const QRect &rect) +void QQnxWindow::setGeometryHelper(const QRect &rect) { qWindowDebug() << Q_FUNC_INFO << "window =" << window() << ", (" << rect.x() << "," << rect.y() << "," << rect.width() << "," << rect.height() << ")"; // Call base class method - QRect oldGeometry = QPlatformWindow::geometry(); QPlatformWindow::setGeometry(rect); // Set window geometry equal to widget geometry - errno = 0; int val[2]; val[0] = rect.x(); val[1] = rect.y(); - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window position, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val), + "Failed to set window position"); - errno = 0; val[0] = rect.width(); val[1] = rect.height(); - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window size, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val), + "Failed to set window size"); // Set viewport size equal to window size - errno = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window source size, errno=%d", errno); - - return oldGeometry; -} - -void QQnxWindow::setOffset(const QPoint &offset) -{ - qWindowDebug() << Q_FUNC_INFO << "window =" << window(); - // Move self and then children. - QRect newGeometry = geometry(); - newGeometry.translate(offset); - - // Call the base class - QPlatformWindow::setGeometry(newGeometry); - - int val[2]; + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val), + "Failed to set window source size"); - errno = 0; - val[0] = newGeometry.x(); - val[1] = newGeometry.y(); - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window position, errno=%d", errno); - - Q_FOREACH (QQnxWindow *childWindow, m_childWindows) - childWindow->setOffset(offset); + screen_flush_context(m_screenContext, 0); } void QQnxWindow::setVisible(bool visible) @@ -214,6 +188,12 @@ void QQnxWindow::setVisible(bool visible) if (m_visible == visible) return; + // The first time through we join a window group if appropriate. + if (m_parentGroupName.isNull() && !m_isTopLevel) { + joinWindowGroup(parent() ? static_cast<QQnxWindow*>(parent())->groupName() + : QByteArray(m_screen->windowGroupName())); + } + m_visible = visible; QQnxWindow *root = this; @@ -222,8 +202,6 @@ void QQnxWindow::setVisible(bool visible) root->updateVisibility(root->m_visible); - window()->requestActivate(); - QWindowSystemInterface::handleExposeEvent(window(), window()->geometry()); if (visible) { @@ -238,11 +216,9 @@ void QQnxWindow::updateVisibility(bool parentVisible) { qWindowDebug() << Q_FUNC_INFO << "parentVisible =" << parentVisible << "window =" << window(); // Set window visibility - errno = 0; int val = (m_visible && parentVisible) ? 1 : 0; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val); - if (result != 0) - qFatal("QQnxWindow: failed to set window visibility, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val), + "Failed to set window visibility"); Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->updateVisibility(m_visible && parentVisible); @@ -252,14 +228,11 @@ void QQnxWindow::setOpacity(qreal level) { qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "opacity =" << level; // Set window global alpha - errno = 0; int val = (int)(level * 255); - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val); - if (result != 0) - qFatal("QQnxWindow: failed to set window global alpha, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val), + "Failed to set global alpha"); - // TODO: How to handle children of this window? If we change all the visibilities, then - // the transparency will look wrong... + screen_flush_context(m_screenContext, 0); } void QQnxWindow::setExposed(bool exposed) @@ -282,15 +255,12 @@ void QQnxWindow::setBufferSize(const QSize &size) qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "size =" << size; // Set window buffer size - errno = 0; - // libscreen fails when creating empty buffers const QSize nonEmptySize = size.isEmpty() ? QSize(1, 1) : size; int val[2] = { nonEmptySize.width(), nonEmptySize.height() }; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window buffer size, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val), + "Failed to set window buffer size"); // Create window buffers if they do not exist if (m_bufferSize.isEmpty()) { @@ -298,24 +268,18 @@ void QQnxWindow::setBufferSize(const QSize &size) if (val[0] == -1) // The platform GL context was not set yet on the window, so we can't procede return; - errno = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window pixel format, errno=%d", errno); + Q_SCREEN_CRITICALERROR( + screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val), + "Failed to set window format"); - errno = 0; - result = screen_create_window_buffers(m_window, MAX_BUFFER_COUNT); - if (result != 0) { - qWarning() << "QQnxWindow: Buffer size was" << size; - qFatal("QQnxWindow: failed to create window buffers, errno=%d", errno); - } + Q_SCREEN_CRITICALERROR(screen_create_window_buffers(m_window, MAX_BUFFER_COUNT), + "Failed to create window buffers"); // check if there are any buffers available int bufferCount = 0; - result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &bufferCount); - - if (result != 0) - qFatal("QQnxWindow: failed to query window buffer count, errno=%d", errno); + Q_SCREEN_CRITICALERROR( + screen_get_window_property_iv(m_window, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &bufferCount), + "Failed to query render buffer count"); if (bufferCount != MAX_BUFFER_COUNT) { qFatal("QQnxWindow: invalid buffer count. Expected = %d, got = %d. You might experience problems.", @@ -338,10 +302,8 @@ void QQnxWindow::setBufferSize(const QSize &size) val[0] = SCREEN_TRANSPARENCY_SOURCE_OVER; } - errno = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val); - if (result != 0) - qFatal("QQnxWindow: failed to set window transparency, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val), + "Failed to set window transparency"); // Cache new buffer size m_bufferSize = nonEmptySize; @@ -366,9 +328,8 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) if (m_screen) { qWindowDebug() << Q_FUNC_INFO << "Moving window to different screen"; m_screen->removeWindow(this); - QQnxIntegration *platformIntegration = static_cast<QQnxIntegration*>(QGuiApplicationPrivate::platformIntegration()); - if ((platformIntegration->options() & QQnxIntegration::RootWindow)) { + if ((QQnxIntegration::options() & QQnxIntegration::RootWindow)) { screen_leave_window_group(m_window); } } @@ -378,25 +339,12 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) platformScreen->addWindow(this); } if (m_isTopLevel) { - // Move window to proper screen/display - errno = 0; + // Move window to proper screen/display screen_display_t display = platformScreen->nativeDisplay(); - int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); - if (result != 0) - qFatal("QQnxWindow: failed to set window display, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display), + "Failed to set window display"); } else { - errno = 0; - int result; - if (!parent()) { - result = screen_join_window_group(m_window, platformScreen->windowGroupName()); - if (result != 0) - qFatal("QQnxWindow: failed to join window group, errno=%d", errno); - } else { - result = screen_join_window_group(m_window, static_cast<QQnxWindow*>(parent())->groupName().constData()); - if (result != 0) - qFatal("QQnxWindow: failed to join window group, errno=%d", errno); - } - Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { // Only subwindows and tooltips need necessarily be moved to another display with the window. if (window()->type() == Qt::SubWindow || window()->type() == Qt::ToolTip) @@ -430,6 +378,11 @@ void QQnxWindow::setParent(const QPlatformWindow *window) if (newParent == m_parentWindow) return; + if (screen()->rootWindow() == this) { + qWarning() << "Application window cannot be reparented"; + return; + } + removeFromParent(); m_parentWindow = newParent; @@ -439,12 +392,12 @@ void QQnxWindow::setParent(const QPlatformWindow *window) setScreen(m_parentWindow->m_screen); m_parentWindow->m_childWindows.push_back(this); + joinWindowGroup(m_parentWindow->groupName()); } else { m_screen->addWindow(this); + joinWindowGroup(QByteArray()); } - adjustBufferSize(); - m_screen->updateHierarchy(); } @@ -478,14 +431,59 @@ void QQnxWindow::lower() void QQnxWindow::requestActivateWindow() { - qWindowDebug() << Q_FUNC_INFO << "window =" << window(); + QQnxWindow *focusWindow = 0; + if (QGuiApplication::focusWindow()) + focusWindow = static_cast<QQnxWindow*>(QGuiApplication::focusWindow()->handle()); + + if (focusWindow == this) + return; + + if (screen()->rootWindow() == this || + (focusWindow && findWindow(focusWindow->nativeHandle()))) { + // If the focus window is a child, we can just set the focus of our own window + // group to our window handle + setFocus(nativeHandle()); + } else { + // In order to receive focus the parent's window group has to give focus to the + // child. If we have several hierarchy layers, we have to do that several times + QQnxWindow *currentWindow = this; + QList<QQnxWindow*> windowList; + while (currentWindow) { + windowList.prepend(currentWindow); + // If we find the focus window, we don't have to go further + if (currentWindow == focusWindow) + break; + + if (currentWindow->parent()){ + currentWindow = static_cast<QQnxWindow*>(currentWindow->parent()); + } else if (screen()->rootWindow() && + screen()->rootWindow()->m_windowGroupName == currentWindow->m_parentGroupName) { + currentWindow = screen()->rootWindow(); + } else { + currentWindow = 0; + } + } - // TODO: Tell screen to set keyboard focus to this window. + // We have to apply the focus from parent to child windows + for (int i = 1; i < windowList.size(); ++i) + windowList.at(i-1)->setFocus(windowList.at(i)->nativeHandle()); + + windowList.last()->setFocus(windowList.last()->nativeHandle()); + } - // Notify that we gained focus. - gainedFocus(); + screen_flush_context(m_screenContext, 0); } +void QQnxWindow::setFocus(screen_window_t newFocusWindow) +{ + screen_group_t screenGroup = 0; + screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_GROUP, + reinterpret_cast<void**>(&screenGroup)); + if (screenGroup) { + screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_KEYBOARD_FOCUS, + reinterpret_cast<void**>(&newFocusWindow)); + } +} void QQnxWindow::setWindowState(Qt::WindowState state) { @@ -507,14 +505,6 @@ void QQnxWindow::propagateSizeHints() qWindowDebug() << Q_FUNC_INFO << ": ignored"; } -void QQnxWindow::gainedFocus() -{ - qWindowDebug() << Q_FUNC_INFO << "window =" << window(); - - // Got focus - QWindowSystemInterface::handleWindowActivated(window()); -} - void QQnxWindow::setMMRendererWindowName(const QString &name) { m_mmRendererWindowName = name; @@ -569,43 +559,43 @@ void QQnxWindow::minimize() void QQnxWindow::setRotation(int rotation) { qWindowDebug() << Q_FUNC_INFO << "angle =" << rotation; - errno = 0; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window rotation, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation), + "Failed to set window rotation"); } void QQnxWindow::initWindow() { // Alpha channel is always pre-multiplied if present - errno = 0; int val = SCREEN_PRE_MULTIPLIED_ALPHA; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val); - if (result != 0) - qFatal("QQnxWindow: failed to set window alpha mode, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val), + "Failed to set alpha mode"); // Set the window swap interval - errno = 0; val = 1; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val); - if (result != 0) - qFatal("QQnxWindow: failed to set window swap interval, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val), + "Failed to set swap interval"); if (window()->flags() & Qt::WindowDoesNotAcceptFocus) { - errno = 0; val = SCREEN_SENSITIVITY_NO_FOCUS; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val); - if (result != 0) - qFatal("QQnxWindow: failed to set window sensitivity, errno=%d", errno); + Q_SCREEN_CHECKERROR( + screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val), + "Failed to set window sensitivity"); } - setScreen(static_cast<QQnxScreen *>(window()->screen()->handle())); + QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window()->screen()->handle()); + setScreen(platformScreen); if (window()->type() == Qt::CoverWindow) { #if defined(Q_OS_BLACKBERRY) && !defined(Q_OS_BLACKBERRY_TABLET) - screen_set_window_property_pv(m_screen->rootWindow()->nativeHandle(), - SCREEN_PROPERTY_ALTERNATE_WINDOW, (void**)&m_window); - m_cover.reset(new QQnxNavigatorCover); + if (platformScreen->rootWindow()) { + screen_set_window_property_pv(m_screen->rootWindow()->nativeHandle(), + SCREEN_PROPERTY_ALTERNATE_WINDOW, (void**)&m_window); + m_cover.reset(new QQnxNavigatorCover); + } else { + qWarning("No root window for cover window"); + } #endif m_exposed = false; } @@ -615,6 +605,8 @@ void QQnxWindow::initWindow() // Qt never calls these setters after creating the window, so we need to do that ourselves here setWindowState(window()->windowState()); + setOpacity(window()->opacity()); + if (window()->parent() && window()->parent()->handle()) setParent(window()->parent()->handle()); @@ -632,10 +624,36 @@ void QQnxWindow::createWindowGroup() m_windowGroupName = QUuid::createUuid().toString().toLatin1(); // Create window group so child windows can be parented by container window - errno = 0; - int result = screen_create_window_group(m_window, m_windowGroupName.constData()); - if (result != 0) - qFatal("QQnxRootWindow: failed to create app window group, errno=%d", errno); + Q_SCREEN_CHECKERROR(screen_create_window_group(m_window, m_windowGroupName.constData()), + "Failed to create window group"); +} + +void QQnxWindow::joinWindowGroup(const QByteArray &groupName) +{ + bool changed = false; + + qWindowDebug() << Q_FUNC_INFO << "group:" << groupName; + + if (!groupName.isEmpty()) { + if (groupName != m_parentGroupName) { + screen_join_window_group(m_window, groupName); + m_parentGroupName = groupName; + changed = true; + } + } else { + if (!m_parentGroupName.isEmpty()) { + screen_leave_window_group(m_window); + changed = true; + } + // By setting to an empty string we'll stop setVisible from trying to + // change our group, we want that to happen only if joinWindowGroup has + // never been called. This allows windows to be created that are not initially + // part of any group. + m_parentGroupName = ""; + } + + if (changed) + screen_flush_context(m_screenContext, 0); } void QQnxWindow::updateZorder(int &topZorder) @@ -651,12 +669,9 @@ void QQnxWindow::updateZorder(int &topZorder) void QQnxWindow::updateZorder(screen_window_t window, int &topZorder) { - errno = 0; - int result = screen_set_window_property_iv(window, SCREEN_PROPERTY_ZORDER, &topZorder); + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(window, SCREEN_PROPERTY_ZORDER, &topZorder), + "Failed to set window z-order"); topZorder++; - - if (result != 0) - qFatal("QQnxWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, window); } void QQnxWindow::applyWindowState() diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index 3c8070b0be..ea7b388e9f 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -78,7 +78,6 @@ public: WId winId() const { return (WId)m_window; } screen_window_t nativeHandle() const { return m_window; } - virtual void adjustBufferSize() = 0; void setBufferSize(const QSize &size); QSize bufferSize() const { return m_bufferSize; } @@ -93,7 +92,6 @@ public: void propagateSizeHints(); - void gainedFocus(); void setMMRendererWindowName(const QString &name); void setMMRendererWindow(screen_window_t handle); void clearMMRendererWindow(); @@ -112,6 +110,7 @@ public: void setRotation(int rotation); QByteArray groupName() const { return m_windowGroupName; } + void joinWindowGroup(const QByteArray &groupName); protected: virtual int pixelFormat() const = 0; @@ -122,22 +121,21 @@ protected: screen_context_t m_screenContext; QScopedPointer<QQnxAbstractCover> m_cover; - QQnxWindow *m_parentWindow; - private: void createWindowGroup(); - QRect setGeometryHelper(const QRect &rect); + void setGeometryHelper(const QRect &rect); void removeFromParent(); - void setOffset(const QPoint &setOffset); void updateVisibility(bool parentVisible); void updateZorder(int &topZorder); void updateZorder(screen_window_t window, int &zOrder); void applyWindowState(); + void setFocus(screen_window_t newFocusWindow); screen_window_t m_window; QSize m_bufferSize; QQnxScreen *m_screen; + QQnxWindow *m_parentWindow; QList<QQnxWindow*> m_childWindows; bool m_visible; bool m_exposed; @@ -146,7 +144,10 @@ private: QString m_mmRendererWindowName; screen_window_t m_mmRendererWindow; + // Group name of window group headed by this window QByteArray m_windowGroupName; + // Group name that we have joined or "" if we've not joined any group. + QByteArray m_parentGroupName; bool m_isTopLevel; }; |