| /*************************************************************************** |
| ** |
| ** Copyright (C) 2012 Research In Motion |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $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> |
| |
| #if defined(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); |
| m_buttonKeys.reserve(ButtonCount - bid_minus); |
| 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("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) { |
| #if defined (QQNXBUTTON_DEBUG) |
| qWarning("QQNX: failed to open buttons pps, errno=%d", errno); |
| #endif |
| return; |
| } |
| |
| m_readNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read); |
| QObject::connect(m_readNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(updateButtonStates())); |
| |
| qButtonDebug("successfully connected to Navigator. fd = %d", 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("received PPS message:\n%s", 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) == "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).contains(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() << "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 |