QNX: Move screen event retrieval to the GUI thread

Instead of retrieving screen events itself, the screen event thread
now turns screen event notification into a Qt signal.  In response to
the signal, the GUI thread processes screen events.  Eliminates the
need to coordinate access to a screen event queue.  Also prepares the
way for getting rid of the screen event thread.  This could be done if
the QNX event dispatcher were switched from poll to MsgReceive
(dispatch).

Change-Id: If5892466d04d67f00828fddaea38b20da5fb18a3
Reviewed-by: Janne Koskinen <janne.p.koskinen@qt.io>
Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
bb10
James McDonnell 2018-04-26 12:33:13 -04:00
parent 1e9da7b3fd
commit fc356a1992
5 changed files with 105 additions and 117 deletions

View File

@ -164,7 +164,8 @@ QQnxIntegration::QQnxIntegration(const QStringList &paramList)
#endif
// Create/start event thread
m_screenEventThread = new QQnxScreenEventThread(ms_screenContext, m_screenEventHandler);
m_screenEventThread = new QQnxScreenEventThread(ms_screenContext);
m_screenEventHandler->setScreenEventThread(m_screenEventThread);
m_screenEventThread->start();
#if QT_CONFIG(qqnx_pps)

View File

@ -197,36 +197,39 @@ void QQnxScreenEventHandler::injectKeyboardEvent(int flags, int sym, int modifie
void QQnxScreenEventHandler::setScreenEventThread(QQnxScreenEventThread *eventThread)
{
m_eventThread = eventThread;
connect(m_eventThread, &QQnxScreenEventThread::eventsPending,
this, &QQnxScreenEventHandler::processEvents);
}
void QQnxScreenEventHandler::processEventsFromScreenThread()
void QQnxScreenEventHandler::processEvents()
{
if (!m_eventThread)
return;
QQnxScreenEventArray *events = m_eventThread->lock();
screen_event_t event = nullptr;
if (screen_create_event(&event) != 0)
return;
for (int i = 0; i < events->size(); ++i) {
screen_event_t event = events->at(i);
if (!event)
continue;
(*events)[i] = 0;
int count = 0;
for (;;) {
if (screen_get_event(m_eventThread->context(), event, 0) != 0)
break;
m_eventThread->unlock();
int type = SCREEN_EVENT_NONE;
screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &type);
if (type == SCREEN_EVENT_NONE)
break;
++count;
long result = 0;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
bool handled = dispatcher && dispatcher->filterNativeEvent(QByteArrayLiteral("screen_event_t"), event, &result);
if (!handled)
handleEvent(event);
screen_destroy_event(event);
m_eventThread->lock();
}
events->clear();
m_eventThread->unlock();
m_eventThread->armEventsPending(count);
screen_destroy_event(event);
}
void QQnxScreenEventHandler::handleKeyboardEvent(screen_event_t event)

View File

@ -74,7 +74,7 @@ protected:
void timerEvent(QTimerEvent *event) override;
private Q_SLOTS:
void processEventsFromScreenThread();
void processEvents();
private:
void handleKeyboardEvent(screen_event_t event);

View File

@ -1,5 +1,6 @@
/***************************************************************************
**
** Copyright (C) 2017 QNX Software Systems. All rights reserved.
** Copyright (C) 2011 - 2012 Research In Motion
** Contact: https://www.qt.io/licensing/
**
@ -55,116 +56,101 @@
#define qScreenEventThreadDebug QT_NO_QDEBUG_MACRO
#endif
QQnxScreenEventThread::QQnxScreenEventThread(screen_context_t context, QQnxScreenEventHandler *screenEventHandler)
: QThread(),
m_screenContext(context),
m_screenEventHandler(screenEventHandler),
m_quit(false)
static const int c_screenCode = _PULSE_CODE_MINAVAIL + 0;
static const int c_armCode = _PULSE_CODE_MINAVAIL + 1;
static const int c_quitCode = _PULSE_CODE_MINAVAIL + 2;
QQnxScreenEventThread::QQnxScreenEventThread(screen_context_t context)
: QThread()
, m_screenContext(context)
{
screenEventHandler->setScreenEventThread(this);
connect(this, SIGNAL(eventPending()), screenEventHandler, SLOT(processEventsFromScreenThread()), Qt::QueuedConnection);
connect(this, SIGNAL(finished()), screenEventHandler, SLOT(processEventsFromScreenThread()), Qt::QueuedConnection);
m_channelId = ChannelCreate(_NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK | _NTO_CHF_PRIVATE);
if (m_channelId == -1) {
qFatal("QQnxScreenEventThread: Can't continue without a channel");
}
m_connectionId = ConnectAttach(0, 0, m_channelId, _NTO_SIDE_CHANNEL, 0);
if (m_connectionId == -1) {
ChannelDestroy(m_channelId);
qFatal("QQnxScreenEventThread: Can't continue without a channel connection");
}
struct sigevent screenEvent;
SIGEV_PULSE_INIT(&screenEvent, m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_screenCode, 0);
screen_notify(m_screenContext, SCREEN_NOTIFY_EVENT, nullptr, &screenEvent);
}
QQnxScreenEventThread::~QQnxScreenEventThread()
{
// block until thread terminates
shutdown();
}
void QQnxScreenEventThread::injectKeyboardEvent(int flags, int sym, int mod, int scan, int cap)
{
QQnxScreenEventHandler::injectKeyboardEvent(flags, sym, mod, scan, cap);
}
QQnxScreenEventArray *QQnxScreenEventThread::lock()
{
m_mutex.lock();
return &m_events;
}
void QQnxScreenEventThread::unlock()
{
m_mutex.unlock();
ConnectDetach(m_connectionId);
ChannelDestroy(m_channelId);
}
void QQnxScreenEventThread::run()
{
qScreenEventThreadDebug("screen event thread started");
int errorCounter = 0;
// loop indefinitely
while (!m_quit) {
screen_event_t event;
// create screen event
Q_SCREEN_CHECKERROR(screen_create_event(&event), "Failed to create screen event");
// block until screen event is available
const int error = screen_get_event(m_screenContext, event, -1);
Q_SCREEN_CRITICALERROR(error, "Failed to get screen event");
// Only allow 50 consecutive errors before we exit the thread
if (error) {
errorCounter++;
if (errorCounter > 50)
m_quit = true;
screen_destroy_event(event);
continue;
} else {
errorCounter = 0;
}
// process received event
// get the event type
int qnxType;
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
qScreenEventThreadDebug("QNX user screen event");
m_quit = true;
} else {
m_mutex.lock();
m_events << event;
m_mutex.unlock();
emit eventPending();
}
while (1) {
struct _pulse msg;
memset(&msg, 0, sizeof(msg));
int receiveId = MsgReceive(m_channelId, &msg, sizeof(msg), nullptr);
if (receiveId == 0 && msg.code == c_quitCode)
break;
else if (receiveId == 0)
handlePulse(msg);
else if (receiveId > 0)
qWarning() << "Unexpected message" << msg.code;
else
qWarning() << "MsgReceive error" << strerror(errno);
}
qScreenEventThreadDebug("screen event thread stopped");
}
// cleanup
m_mutex.lock();
Q_FOREACH (screen_event_t event, m_events) {
screen_destroy_event(event);
void QQnxScreenEventThread::armEventsPending(int count)
{
MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_armCode, count);
}
void QQnxScreenEventThread::handleScreenPulse(const struct _pulse &msg)
{
Q_UNUSED(msg);
++m_screenPulsesSinceLastArmPulse;
if (m_emitNeededOnNextScreenPulse) {
m_emitNeededOnNextScreenPulse = false;
Q_EMIT eventsPending();
}
m_events.clear();
m_mutex.unlock();
}
void QQnxScreenEventThread::handleArmPulse(const struct _pulse &msg)
{
if (msg.value.sival_int == 0 && m_screenPulsesSinceLastArmPulse == 0) {
m_emitNeededOnNextScreenPulse = true;
} else {
m_screenPulsesSinceLastArmPulse = 0;
m_emitNeededOnNextScreenPulse = false;
Q_EMIT eventsPending();
}
}
void QQnxScreenEventThread::handlePulse(const struct _pulse &msg)
{
if (msg.code == c_screenCode)
handleScreenPulse(msg);
else if (msg.code == c_armCode)
handleArmPulse(msg);
else
qWarning() << "Unexpected pulse" << msg.code;
}
void QQnxScreenEventThread::shutdown()
{
screen_event_t event;
// create screen event
Q_SCREEN_CHECKERROR(screen_create_event(&event),
"Failed to create screen event");
// set the event type as user
int type = SCREEN_EVENT_USER;
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
Q_SCREEN_CHECKERROR(screen_send_event(m_screenContext, event, getpid()),
"Failed to set screen event type");
// cleanup
screen_destroy_event(event);
MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_quitCode, 0);
qScreenEventThreadDebug("screen event thread shutdown begin");

View File

@ -1,5 +1,6 @@
/***************************************************************************
**
** Copyright (C) 2017 QNX Software Systems. All rights reserved.
** Copyright (C) 2011 - 2012 Research In Motion
** Contact: https://www.qt.io/licensing/
**
@ -47,37 +48,34 @@
QT_BEGIN_NAMESPACE
class QQnxScreenEventHandler;
typedef QVarLengthArray<screen_event_t, 64> QQnxScreenEventArray;
class QQnxScreenEventThread : public QThread
{
Q_OBJECT
public:
QQnxScreenEventThread(screen_context_t context, QQnxScreenEventHandler *screenEventHandler);
QQnxScreenEventThread(screen_context_t context);
~QQnxScreenEventThread();
static void injectKeyboardEvent(int flags, int sym, int mod, int scan, int cap);
QQnxScreenEventArray *lock();
void unlock();
screen_context_t context() const { return m_screenContext; }
void armEventsPending(int count);
protected:
void run() override;
Q_SIGNALS:
void eventPending();
void eventsPending();
private:
void handleScreenPulse(const struct _pulse &msg);
void handleArmPulse(const struct _pulse &msg);
void handlePulse(const struct _pulse &msg);
void shutdown();
int m_channelId;
int m_connectionId;
screen_context_t m_screenContext;
QMutex m_mutex;
QQnxScreenEventArray m_events;
QQnxScreenEventHandler *m_screenEventHandler;
bool m_quit;
bool m_emitNeededOnNextScreenPulse = true;
int m_screenPulsesSinceLastArmPulse = 0;
};
QT_END_NAMESPACE