QNX: Enable support for hardware buttons in QPA plugin
authorSean Harmer <sean.harmer@kdab.com>
Tue, 4 Sep 2012 18:58:27 +0000 (19:58 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 7 Sep 2012 16:41:22 +0000 (18:41 +0200)
Change-Id: I3de18c3fdcfdacddc375b70800b34b6a8d16ac41
Reviewed-by: Qt Doc Bot <qt_docbot@qt-project.org>
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Thomas McGuire <thomas.mcguire@kdab.com>
Reviewed-by: Kevin Krammer <kevin.krammer@kdab.com>
src/plugins/platforms/qnx/qnx.pro
src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp [new file with mode: 0644]
src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h [new file with mode: 0644]
src/plugins/platforms/qnx/qqnxintegration.cpp
src/plugins/platforms/qnx/qqnxintegration.h

index 5298824..dc16016 100644 (file)
@@ -23,6 +23,7 @@ CONFIG(blackberry) {
 # Uncomment these to enable debugging output for various aspects of the plugin
 #DEFINES += QQNXBPSEVENTFILTER_DEBUG
 #DEFINES += QQNXBUFFER_DEBUG
+#DEFINES += QQNXBUTTON_DEBUG
 #DEFINES += QQNXCLIPBOARD_DEBUG
 #DEFINES += QQNXFILEDIALOGHELPER_DEBUG
 #DEFINES += QQNXGLBACKINGSTORE_DEBUG
@@ -109,12 +110,14 @@ CONFIG(qqnx_pps) {
     SOURCES += qqnxnavigatorpps.cpp \
                qqnxnavigatoreventnotifier.cpp \
                qqnxvirtualkeyboardpps.cpp \
-               qqnxclipboard.cpp
+               qqnxclipboard.cpp \
+               qqnxbuttoneventnotifier.cpp
 
     HEADERS += qqnxnavigatorpps.h \
                qqnxnavigatoreventnotifier.h \
                qqnxvirtualkeyboardpps.h \
-               qqnxclipboard.h
+               qqnxclipboard.h \
+               qqnxbuttoneventnotifier.h
 
     LIBS += -lpps -lclipboard
 
diff --git a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp
new file mode 100644 (file)
index 0000000..f4e7237
--- /dev/null
@@ -0,0 +1,221 @@
+/***************************************************************************
+**
+** Copyright (C) 2012 Research In Motion
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqnxbuttoneventnotifier.h"
+
+#include <QtGui/QGuiApplication>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QMetaEnum>
+#include <QtCore/QSocketNotifier>
+#include <QtCore/private/qcore_unix_p.h>
+
+#ifdef QQNXBUTTON_DEBUG
+#define qButtonDebug qDebug
+#else
+#define qButtonDebug QT_NO_QDEBUG_MACRO
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const char *ppsPath = "/pps/system/buttons/status";
+static const int ppsBufferSize = 256;
+
+QQnxButtonEventNotifier::QQnxButtonEventNotifier(QObject *parent)
+    : QObject(parent),
+      m_fd(-1),
+      m_readNotifier(0)
+{
+    // Set initial state of buttons to ButtonUp and
+    // fetch the new button ids
+    int enumeratorIndex = QQnxButtonEventNotifier::staticMetaObject.indexOfEnumerator(QByteArrayLiteral("ButtonId"));
+    QMetaEnum enumerator = QQnxButtonEventNotifier::staticMetaObject.enumerator(enumeratorIndex);
+    for (int buttonId = bid_minus; buttonId < ButtonCount; ++buttonId) {
+        m_buttonKeys.append(enumerator.valueToKey(buttonId));
+        m_state[buttonId] = ButtonUp;
+    }
+}
+
+QQnxButtonEventNotifier::~QQnxButtonEventNotifier()
+{
+    close();
+}
+
+void QQnxButtonEventNotifier::start()
+{
+    qButtonDebug() << Q_FUNC_INFO << "starting hardware button event processing";
+    if (m_fd != -1)
+        return;
+
+    // Open the pps interface
+    errno = 0;
+    m_fd = qt_safe_open(ppsPath, O_RDONLY);
+    if (m_fd == -1) {
+        qWarning("QQNX: failed to open buttons pps, errno=%d", errno);
+        return;
+    }
+
+    m_readNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read);
+    QObject::connect(m_readNotifier, SIGNAL(activated(int)), this, SLOT(updateButtonStates()));
+
+    qButtonDebug() << Q_FUNC_INFO << "successfully connected to Navigator. fd =" << m_fd;
+}
+
+void QQnxButtonEventNotifier::updateButtonStates()
+{
+    // Allocate buffer for pps data
+    char buffer[ppsBufferSize];
+
+    // Attempt to read pps data
+    errno = 0;
+    int bytes = qt_safe_read(m_fd, buffer, ppsBufferSize - 1);
+    qButtonDebug() << "Read" << bytes << "bytes of data";
+    if (bytes == -1) {
+        qWarning("QQNX: failed to read hardware buttons pps object, errno=%d", errno);
+        return;
+    }
+
+    // We seem to get a spurious read notification after the real one. Ignore it
+    if (bytes == 0)
+        return;
+
+    // Ensure data is null terminated
+    buffer[bytes] = '\0';
+
+    qButtonDebug() << Q_FUNC_INFO << "received PPS message:\n" << buffer;
+
+    // Process received message
+    QByteArray ppsData = QByteArray::fromRawData(buffer, bytes);
+    QHash<QByteArray, QByteArray> fields;
+    if (!parsePPS(ppsData, &fields))
+        return;
+
+    // Update our state and inject key events as needed
+    for (int buttonId = bid_minus; buttonId < ButtonCount; ++buttonId) {
+        // Extract the new button state
+        QByteArray key = m_buttonKeys.at(buttonId);
+        ButtonState newState = (fields.value(key) == QByteArrayLiteral("b_up") ? ButtonUp : ButtonDown);
+
+        // If state has changed, update our state and inject a keypress event
+        if (m_state[buttonId] != newState) {
+            qButtonDebug() << "Hardware button event: button =" << key << "state =" << fields.value(key);
+            m_state[buttonId] = newState;
+
+            // Is it a key press or key release event?
+            QEvent::Type type = (newState == ButtonDown) ? QEvent::KeyPress : QEvent::KeyRelease;
+
+            Qt::Key key;
+            switch (buttonId) {
+                case bid_minus:
+                    key = Qt::Key_VolumeDown;
+                    break;
+
+                case bid_playpause:
+                    key = Qt::Key_Play;
+                    break;
+
+                case bid_plus:
+                    key = Qt::Key_VolumeUp;
+                    break;
+
+                case bid_power:
+                    key = Qt::Key_PowerDown;
+                    break;
+
+                default:
+                    qButtonDebug() << "Unknown hardware button";
+                    continue;
+            }
+
+            // No modifiers
+            Qt::KeyboardModifiers modifier = Qt::NoModifier;
+
+            // Post the event
+            QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), type, key, modifier);
+        }
+    }
+}
+
+void QQnxButtonEventNotifier::close()
+{
+    delete m_readNotifier;
+    m_readNotifier = 0;
+
+    if (m_fd != -1) {
+        qt_safe_close(m_fd);
+        m_fd = -1;
+    }
+}
+
+bool QQnxButtonEventNotifier::parsePPS(const QByteArray &ppsData, QHash<QByteArray, QByteArray> *messageFields) const
+{
+    // tokenize pps data into lines
+    QList<QByteArray> lines = ppsData.split('\n');
+
+    // validate pps object
+    if (lines.size() == 0 || lines.at(0) != QByteArrayLiteral("@status")) {
+        qWarning("QQNX: unrecognized pps object, data=%s", ppsData.constData());
+        return false;
+    }
+
+    // parse pps object attributes and extract values
+    for (int i = 1; i < lines.size(); i++) {
+
+        // tokenize current attribute
+        const QByteArray &attr = lines.at(i);
+
+        qButtonDebug() << Q_FUNC_INFO << "attr=" << attr;
+
+        int doubleColon = attr.indexOf(QByteArrayLiteral("::"));
+        if (doubleColon == -1) {
+            // abort - malformed attribute
+            continue;
+        }
+
+        QByteArray key = attr.left(doubleColon);
+        QByteArray value = attr.mid(doubleColon + 2);
+        messageFields->insert(key, value);
+    }
+    return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h
new file mode 100644 (file)
index 0000000..01b9a72
--- /dev/null
@@ -0,0 +1,90 @@
+/***************************************************************************
+**
+** Copyright (C) 2012 Research In Motion
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQNXBUTTONSEVENTNOTIFIER_H
+#define QQNXBUTTONSEVENTNOTIFIER_H
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QSocketNotifier;
+
+class QQnxButtonEventNotifier : public QObject
+{
+    Q_OBJECT
+    Q_ENUMS(ButtonId)
+public:
+    enum ButtonId {
+        bid_minus = 0,
+        bid_playpause,
+        bid_plus,
+        bid_power,
+        ButtonCount
+    };
+
+    enum ButtonState {
+        ButtonUp,
+        ButtonDown
+    };
+
+    explicit QQnxButtonEventNotifier(QObject *parent = 0);
+    ~QQnxButtonEventNotifier();
+
+public Q_SLOTS:
+    void start();
+
+private Q_SLOTS:
+    void updateButtonStates();
+
+private:
+    void close();
+    bool parsePPS(const QByteArray &ppsData, QHash<QByteArray, QByteArray> *messageFields) const;
+
+    int m_fd;
+    QSocketNotifier *m_readNotifier;
+    ButtonState m_state[ButtonCount];
+    QList<QByteArray> m_buttonKeys;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQNXBUTTONSEVENTNOTIFIER_H
index b988020..b3bd4e3 100644 (file)
@@ -62,6 +62,7 @@
 #endif
 
 #if defined(QQNX_PPS)
+#  include "qqnxbuttoneventnotifier.h"
 #  include "qqnxnavigatoreventnotifier.h"
 #  include "qqnxclipboard.h"
 
@@ -112,6 +113,7 @@ QQnxIntegration::QQnxIntegration()
 #if defined(QQNX_PPS)
     , m_navigatorEventNotifier(0)
     , m_inputContext(0)
+    , m_buttonsNotifier(new QQnxButtonEventNotifier())
 #endif
     , m_services(0)
     , m_fontDatabase(new QGenericUnixFontDatabase())
@@ -210,6 +212,11 @@ QQnxIntegration::QQnxIntegration()
 #endif
     }
 
+#if defined(QQNX_PPS)
+    // delay invocation of start() to the time the event loop is up and running
+    // needed to have the QThread internals of the main thread properly initialized
+    QMetaObject::invokeMethod(m_buttonsNotifier, "start", Qt::QueuedConnection);
+#endif
 }
 
 QQnxIntegration::~QQnxIntegration()
@@ -218,6 +225,9 @@ QQnxIntegration::~QQnxIntegration()
     delete m_nativeInterface;
 
 #if defined(QQNX_PPS)
+    // Destroy the hardware button notifier
+    delete m_buttonsNotifier;
+
     // Destroy input context
     delete m_inputContext;
 #endif
index fe2447c..361d4fd 100644 (file)
@@ -65,6 +65,7 @@ class QQnxServices;
 #if defined(QQNX_PPS)
 class QQnxInputContext;
 class QQnxNavigatorEventNotifier;
+class QQnxButtonEventNotifier;
 #endif
 
 #if !defined(QT_NO_CLIPBOARD)
@@ -132,6 +133,7 @@ private:
 #if defined(QQNX_PPS)
     QQnxNavigatorEventNotifier *m_navigatorEventNotifier;
     QQnxInputContext *m_inputContext;
+    QQnxButtonEventNotifier *m_buttonsNotifier;
 #endif
     QQnxServices *m_services;
     QPlatformFontDatabase *m_fontDatabase;