Integrate Blackberry Platform Services (BPS) with Qt event loop.
authorJeff Kehres <jkehres@rim.com>
Fri, 23 Mar 2012 14:43:44 +0000 (10:43 -0400)
committerQt by Nokia <qt-info@nokia.com>
Tue, 24 Apr 2012 17:36:55 +0000 (19:36 +0200)
This ensures interoperability between the Blackbery C and C++ APIs
and makes it easier to expose platform services in C++ that are
exposed in BPS - since events from both APIs can be processed on
the same thread.

Change-Id: I7270adc64c26396f66d9126141500d5e58be51e7
Reviewed-by: Kevin Krammer <kevin.krammer@kdab.com>
Reviewed-by: Giuseppe D'Angelo <dangelog@gmail.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
src/corelib/kernel/kernel.pri
src/corelib/kernel/qcoreapplication.cpp
src/corelib/kernel/qeventdispatcher_blackberry.cpp [new file with mode: 0644]
src/corelib/kernel/qeventdispatcher_blackberry_p.h [new file with mode: 0644]
src/corelib/thread/qthread_unix.cpp

index 4b81087..bef2929 100644 (file)
@@ -136,3 +136,10 @@ vxworks {
                 kernel/qfunctions_vxworks.h
 }
 
+blackberry {
+        SOURCES += \
+                kernel/qeventdispatcher_blackberry.cpp
+        HEADERS += \
+                kernel/qeventdispatcher_blackberry_p.h
+}
+
index d188ccb..95c80ca 100644 (file)
 #include <private/qlocale_p.h>
 
 #if defined(Q_OS_UNIX)
-#  if !defined(QT_NO_GLIB)
-#    include "qeventdispatcher_glib_p.h"
+#  if defined(Q_OS_BLACKBERRY)
+#    include "qeventdispatcher_blackberry_p.h"
+#  else
+#    if !defined(QT_NO_GLIB)
+#      include "qeventdispatcher_glib_p.h"
+#    endif
+#    include "qeventdispatcher_unix_p.h"
 #  endif
-#  include "qeventdispatcher_unix_p.h"
 #endif
 
 #ifdef Q_OS_WIN
@@ -328,12 +332,16 @@ void QCoreApplicationPrivate::createEventDispatcher()
 {
     Q_Q(QCoreApplication);
 #if defined(Q_OS_UNIX)
+#  if defined(Q_OS_BLACKBERRY)
+    eventDispatcher = new QEventDispatcherBlackberry(q);
+#  else
 #  if !defined(QT_NO_GLIB)
     if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported())
         eventDispatcher = new QEventDispatcherGlib(q);
     else
 #  endif
         eventDispatcher = new QEventDispatcherUNIX(q);
+#  endif
 #elif defined(Q_OS_WIN)
     eventDispatcher = new QEventDispatcherWin32(q);
 #else
diff --git a/src/corelib/kernel/qeventdispatcher_blackberry.cpp b/src/corelib/kernel/qeventdispatcher_blackberry.cpp
new file mode 100644 (file)
index 0000000..462e359
--- /dev/null
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Research In Motion
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** 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 "qeventdispatcher_blackberry_p.h"
+#include "qsocketnotifier.h"
+#include "qdebug.h"
+
+#include <bps/bps.h>
+#include <bps/event.h>
+
+struct bpsIOHandlerData {
+    bpsIOHandlerData()
+        : count(0), readfds(0), writefds(0), exceptfds(0)
+    {
+    }
+
+    int count;
+    fd_set *readfds;
+    fd_set *writefds;
+    fd_set *exceptfds;
+};
+
+static int bpsIOReadyDomain = -1;
+
+static int bpsIOHandler(int fd, int io_events, void *data)
+{
+    // decode callback payload
+    bpsIOHandlerData *ioData = static_cast<bpsIOHandlerData*>(data);
+
+    // check if first file is ready
+    bool firstReady = (ioData->count == 0);
+
+    // update ready state of file
+    if (io_events & BPS_IO_INPUT) {
+        FD_SET(fd, ioData->readfds);
+        ioData->count++;
+    }
+
+    if (io_events & BPS_IO_OUTPUT) {
+        FD_SET(fd, ioData->writefds);
+        ioData->count++;
+    }
+
+    if (io_events & BPS_IO_EXCEPT) {
+        FD_SET(fd, ioData->exceptfds);
+        ioData->count++;
+    }
+
+    // force bps_get_event() to return immediately by posting an event to ourselves;
+    // but this only needs to happen once if multiple files become ready at the same time
+    if (firstReady) {
+        // create IO ready event
+        bps_event_t *event;
+        int result = bps_event_create(&event, bpsIOReadyDomain, 0, NULL, NULL);
+        if (result != BPS_SUCCESS) {
+            qWarning("QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberry: bps_event_create() failed");
+            return BPS_FAILURE;
+        }
+
+        // post IO ready event to our thread
+        result = bps_push_event(event);
+        if (result != BPS_SUCCESS) {
+            qWarning("QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberry: bps_push_event() failed");
+            bps_event_destroy(event);
+            return BPS_FAILURE;
+        }
+    }
+
+    return BPS_SUCCESS;
+}
+
+QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberryPrivate()
+    : ioData(new bpsIOHandlerData)
+{
+    // prepare to use BPS
+    int result = bps_initialize();
+    if (result != BPS_SUCCESS)
+        qFatal("QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberry: bps_initialize() failed");
+
+    // get domain for IO ready events - ignoring race condition here for now
+    if (bpsIOReadyDomain == -1) {
+        bpsIOReadyDomain = bps_register_domain();
+        if (bpsIOReadyDomain == -1)
+            qWarning("QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberry: bps_register_domain() failed");
+    }
+
+    // \TODO Reinstate this when bps is fixed. See comment in select() below.
+    // Register thread_pipe[0] with bps
+    /*
+    int io_events = BPS_IO_INPUT;
+    result = bps_add_fd(thread_pipe[0], io_events, &bpsIOHandler, ioData.data());
+    if (result != BPS_SUCCESS)
+        qWarning() << Q_FUNC_INFO << "bps_add_fd() failed";
+    */
+}
+
+QEventDispatcherBlackberryPrivate::~QEventDispatcherBlackberryPrivate()
+{
+    // we're done using BPS
+    bps_shutdown();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+QEventDispatcherBlackberry::QEventDispatcherBlackberry(QObject *parent)
+    : QEventDispatcherUNIX(*new QEventDispatcherBlackberryPrivate, parent)
+{
+}
+
+QEventDispatcherBlackberry::QEventDispatcherBlackberry(QEventDispatcherBlackberryPrivate &dd, QObject *parent)
+    : QEventDispatcherUNIX(dd, parent)
+{
+}
+
+QEventDispatcherBlackberry::~QEventDispatcherBlackberry()
+{
+}
+
+void QEventDispatcherBlackberry::registerSocketNotifier(QSocketNotifier *notifier)
+{
+    Q_ASSERT(notifier);
+
+    // Call the base Unix implementation. Needed to allow select() to be called correctly
+    QEventDispatcherUNIX::registerSocketNotifier(notifier);
+
+    // Register the fd with bps
+    int sockfd = notifier->socket();
+    int type = notifier->type();
+
+    int io_events = 0;
+    switch (type) {
+    case QSocketNotifier::Read:
+        io_events |= BPS_IO_INPUT;
+        break;
+    case QSocketNotifier::Write:
+        io_events |= BPS_IO_OUTPUT;
+        break;
+    case QSocketNotifier::Exception:
+    default:
+        io_events |= BPS_IO_EXCEPT;
+        break;
+    }
+
+    Q_D(QEventDispatcherBlackberry);
+    int result = bps_add_fd(sockfd, io_events, &bpsIOHandler, d->ioData.data());
+    if (result != BPS_SUCCESS)
+        qWarning() << Q_FUNC_INFO << "bps_add_fd() failed";
+}
+
+void QEventDispatcherBlackberry::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+    // Unregister the fd with bps
+    int sockfd = notifier->socket();
+    int result = bps_remove_fd(sockfd);
+    if (result != BPS_SUCCESS)
+        qWarning() << Q_FUNC_INFO << "bps_remove_fd() failed";
+
+    // Allow the base Unix implementation to unregister the fd too
+    QEventDispatcherUNIX::unregisterSocketNotifier(notifier);
+}
+
+int QEventDispatcherBlackberry::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+                                       timeval *timeout)
+{
+    Q_UNUSED(nfds);
+
+    // prepare file sets for bps callback
+    Q_D(QEventDispatcherBlackberry);
+    d->ioData->count = 0;
+    d->ioData->readfds = readfds;
+    d->ioData->writefds = writefds;
+    d->ioData->exceptfds = exceptfds;
+
+    // \TODO Remove this when bps is fixed
+    //
+    // Work around a bug in BPS with which if we register the thread_pipe[0] fd with bps in the
+    // private class' ctor once only then we get spurious notifications that thread_pipe[0] is
+    // ready for reading. The first time the notification is correct and the pipe is emptied in
+    // the calling doSelect() function. The 2nd notification is an error and the resulting attempt
+    // to read and call to wakeUps.testAndSetRelease(1, 0) fails as there has been no intervening
+    // call to QEventDispatcherUNIX::wakeUp().
+    //
+    // Registering thread_pipe[0] here and unregistering it at the end of this call works around
+    // this issue.
+    int io_events = BPS_IO_INPUT;
+    int result = bps_add_fd(d->thread_pipe[0], io_events, &bpsIOHandler, d->ioData.data());
+    if (result != BPS_SUCCESS)
+        qWarning() << Q_FUNC_INFO << "bps_add_fd() failed";
+
+    // reset all file sets
+    if (readfds)
+        FD_ZERO(readfds);
+
+    if (writefds)
+        FD_ZERO(writefds);
+
+    if (exceptfds)
+        FD_ZERO(exceptfds);
+
+    // convert timeout to milliseconds
+    int timeout_ms = -1;
+    if (timeout)
+        timeout_ms = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
+
+    // wait for event or file to be ready
+    bps_event_t *event = NULL;
+    result = bps_get_event(&event, timeout_ms);
+    if (result != BPS_SUCCESS)
+        qWarning("QEventDispatcherBlackberry::select: bps_get_event() failed");
+
+    // pass all received events through filter - except IO ready events
+    if (event && bps_event_get_domain(event) != bpsIOReadyDomain)
+        filterEvent((void*)event);
+
+    // \TODO Remove this when bps is fixed (see comment above)
+    result = bps_remove_fd(d->thread_pipe[0]);
+    if (result != BPS_SUCCESS)
+        qWarning() << Q_FUNC_INFO << "bps_remove_fd() failed";
+
+    // the number of bits set in the file sets
+    return d->ioData->count;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_blackberry_p.h b/src/corelib/kernel/qeventdispatcher_blackberry_p.h
new file mode 100644 (file)
index 0000000..b996bc7
--- /dev/null
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Research In Motion
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** 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 QEVENTDISPATCHER_BLACKBERRY_P_H
+#define QEVENTDISPATCHER_BLACKBERRY_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qeventdispatcher_unix_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherBlackberryPrivate;
+
+class Q_CORE_EXPORT QEventDispatcherBlackberry : public QEventDispatcherUNIX
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QEventDispatcherBlackberry)
+
+public:
+    explicit QEventDispatcherBlackberry(QObject *parent = 0);
+    ~QEventDispatcherBlackberry();
+
+    void registerSocketNotifier(QSocketNotifier *notifier);
+    void unregisterSocketNotifier(QSocketNotifier *notifier);
+
+protected:
+    QEventDispatcherBlackberry(QEventDispatcherBlackberryPrivate &dd, QObject *parent = 0);
+
+    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+               timeval *timeout);
+};
+
+struct bpsIOHandlerData;
+
+class Q_CORE_EXPORT QEventDispatcherBlackberryPrivate : public QEventDispatcherUNIXPrivate
+{
+    Q_DECLARE_PUBLIC(QEventDispatcherBlackberry)
+
+public:
+    QEventDispatcherBlackberryPrivate();
+    ~QEventDispatcherBlackberryPrivate();
+
+    QScopedPointer<bpsIOHandlerData> ioData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_BLACKBERRY_P_H
index 3af54c7..a47115c 100644 (file)
 #include "qplatformdefs.h"
 
 #include <private/qcoreapplication_p.h>
-#if !defined(QT_NO_GLIB)
-#  include "../kernel/qeventdispatcher_glib_p.h"
-#endif
 
-#include <private/qeventdispatcher_unix_p.h>
+#if defined(Q_OS_BLACKBERRY)
+#  include <private/qeventdispatcher_blackberry_p.h>
+#else
+#  if !defined(QT_NO_GLIB)
+#    include "../kernel/qeventdispatcher_glib_p.h"
+#  endif
+#  include <private/qeventdispatcher_unix_p.h>
+#endif
 
 #include "qthreadstorage.h"
 
@@ -248,6 +252,9 @@ typedef void*(*QtThreadCallback)(void*);
 
 void QThreadPrivate::createEventDispatcher(QThreadData *data)
 {
+#if defined(Q_OS_BLACKBERRY)
+    data->eventDispatcher = new QEventDispatcherBlackberry;
+#else
 #if !defined(QT_NO_GLIB)
     if (qgetenv("QT_NO_GLIB").isEmpty()
         && qgetenv("QT_NO_THREADED_GLIB").isEmpty()
@@ -256,6 +263,8 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data)
     else
 #endif
     data->eventDispatcher = new QEventDispatcherUNIX;
+#endif
+
     data->eventDispatcher->startingUp();
 }