From 834e9823fc727d89c6b8e959dee29efa617a5aec Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Tue, 4 Sep 2012 19:58:27 +0100 Subject: [PATCH] QNX: Enable support for hardware buttons in QPA plugin Change-Id: I3de18c3fdcfdacddc375b70800b34b6a8d16ac41 Reviewed-by: Qt Doc Bot Reviewed-by: Giuseppe D'Angelo Reviewed-by: Thomas McGuire Reviewed-by: Kevin Krammer --- src/plugins/platforms/qnx/qnx.pro | 7 +- .../platforms/qnx/qqnxbuttoneventnotifier.cpp | 221 +++++++++++++++++++++ .../platforms/qnx/qqnxbuttoneventnotifier.h | 90 +++++++++ src/plugins/platforms/qnx/qqnxintegration.cpp | 10 + src/plugins/platforms/qnx/qqnxintegration.h | 2 + 5 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp create mode 100644 src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 5298824..dc16016 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -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 index 0000000..f4e7237 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp @@ -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 +#include + +#include +#include +#include +#include + +#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 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 *messageFields) const +{ + // tokenize pps data into lines + QList 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 index 0000000..01b9a72 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h @@ -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 + +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 *messageFields) const; + + int m_fd; + QSocketNotifier *m_readNotifier; + ButtonState m_state[ButtonCount]; + QList m_buttonKeys; +}; + +QT_END_NAMESPACE + +#endif // QQNXBUTTONSEVENTNOTIFIER_H diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index b988020..b3bd4e3 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -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 diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index fe2447c..361d4fd 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -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; -- 2.7.4