pidgin/quail/quail-redux

Parents 1b96f1615999
Children 692b90938442
We are going to have two event levels, 1) Quail/Pidgin and 2) Windows/GLib. That simplifies things and means I can keep the windows specific parts in its own class
--- a/src/QuailEventLoop.cpp Fri Sep 13 14:11:27 2013 +0100
+++ b/src/QuailEventLoop.cpp Mon Sep 16 16:27:25 2013 +0100
@@ -21,157 +21,114 @@
*/
#include "QuailEventLoop.h"
#include <QDebug>
+#include <QMap>
+
+typedef struct
+{
+ guint handle;
+
+ union
+ {
+ QQuailTimer *timer;
+ QQuailInputNotifier *notifier;
+ };
+
+} QQuailSourceInfo;
static gboolean qQuailSourceRemove(guint handle);
-static gboolean qQuailTimeoutRemove(guint handle);
-static QuailEventLoop *eventLoop = NULL;
+static guint nextSourceId = 0;
+static QMap<guint, QQuailSourceInfo*> m_sources;
-QQuailTimer::QQuailTimer(GSourceFunc func, gpointer data)
- : QTimer(eventLoop), func(func), userData(data)
+QQuailTimer::QQuailTimer(guint sourceId, GSourceFunc func, gpointer data)
+ : QTimer(), sourceId(sourceId), func(func), userData(data)
{
- connect(this, SIGNAL(timeout()),
- this, SLOT(update()));
+ connect(this, SIGNAL(timeout()),
+ this, SLOT(update()));
}
void
QQuailTimer::update()
{
- if (!func(userData))
- qQuailTimeoutRemove(sourceId);
-}
-
-void
-QQuailTimer::setHandle(guint newSourceId)
-{
- sourceId(newSourceId);
+ if (!func(userData))
+ qQuailSourceRemove(sourceId);
}
QQuailInputNotifier::QQuailInputNotifier(int fd,
PurpleInputCondition cond,
PurpleInputFunction func,
gpointer userData)
- : QObject(eventLoop), func(func), userData(userData), readNotifier(NULL),
- writeNotifier(NULL)
+ : QObject(), func(func), userData(userData), readNotifier(NULL),
+ writeNotifier(NULL)
{
//qDebug() << "QQuailInputNotifier::QQuailInputNotifier";
if (cond & PURPLE_INPUT_READ)
- {
+ {
//qDebug() << "QQuailInputNotifier::QQuailInputNotifier::READ";
- readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read);
+ readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read);
- connect(readNotifier, SIGNAL(activated(int)),
- this, SLOT(ioInvoke(int)));
- }
+ connect(readNotifier, SIGNAL(activated(int)),
+ this, SLOT(ioInvoke(int)));
+ }
if (cond & PURPLE_INPUT_WRITE)
- {
+ {
//qDebug() << "QQuailInputNotifier::QQuailInputNotifier::WRITE";
- writeNotifier = new QSocketNotifier(fd, QSocketNotifier::Write);
+ writeNotifier = new QSocketNotifier(fd, QSocketNotifier::Write);
- connect(writeNotifier, SIGNAL(activated(int)),
- this, SLOT(ioInvoke(int)));
- }
+ connect(writeNotifier, SIGNAL(activated(int)),
+ this, SLOT(ioInvoke(int)));
+ }
}
QQuailInputNotifier::~QQuailInputNotifier()
{
- if (readNotifier != NULL)
- delete readNotifier;
+ if (readNotifier != NULL)
+ delete readNotifier;
- if (writeNotifier != NULL)
- delete writeNotifier;
+ if (writeNotifier != NULL)
+ delete writeNotifier;
}
void
QQuailInputNotifier::ioInvoke(int fd)
{
//qDebug() << "QQuailInputNotifier::ioInvoke";
- int cond = 0;
+ int cond = 0;
- if (readNotifier != NULL)
+ if (readNotifier != NULL)
cond |= PURPLE_INPUT_READ;
- if (writeNotifier != NULL)
+ if (writeNotifier != NULL)
cond |= PURPLE_INPUT_WRITE;
- func(userData, fd, (PurpleInputCondition)cond);
-}
-
-QuailEventLoop::QuailEventLoop(QObject *parent) :
- QObject(parent)
-{
- eventLoop = this;
-}
-
-bool QuailEventLoop::processEvents(QEventLoop::ProcessEventsFlags flags)
-{
- //TODO: Fill this in
-}
-
-bool QuailEventLoop::hasPendingEvents()
-{
- return (m_timers.count() + m_sources.count()) > 0;
+ func(userData, fd, (PurpleInputCondition)cond);
}
-void QuailEventLoop::registerSocketNotifier(QSocketNotifier *notifier)
-{
- //qQuailInputAdd
-}
-
-void QuailEventLoop::unregisterSocketNotifier(QSocketNotifier *notifier)
-{
- //qQuailSourceRemove
-}
-
-void QuailEventLoop::registerTimer(int timerId, int interval, QObject *object)
-{
- //qQuailTimeoutAdd
-}
-
-bool QuailEventLoop::unregisterTimer(int timerId)
-{
- qQuailTimeoutRemove(timerId);
-}
-
-bool QuailEventLoop::unregisterTimers(QObject *object)
-{
- qQuailTimeoutRemove(m_timers.indexOf(object));
-}
-
-QList<QAbstractEventDispatcher::TimerInfo>
-QuailEventLoop::registeredTimers(QObject *object) const
-{
- Q_UNUSED(object);
- /* Not implemented, we won't use this. It's only used for
- transfering QObject from one thread to another. */
- return QList<QAbstractEventDispatcher::TimerInfo>();
-}
static guint
qQuailTimeoutAdd(guint interval, GSourceFunc func, gpointer data)
{
//qDebug() << "QQuailInputNotifier::qQuailTimeoutAdd";
- QQuailTimer *timer = new QQuailTimer(func, data);
- eventLoop->m_timers.append(timer);
- guint handle = eventLoop->m_timers.lastIndexOf(timer);
- timer->setHandle(handle);
- timer->start(interval);
+ QQuailSourceInfo *info = new QQuailSourceInfo;
+
+ info->handle = nextSourceId++;
- return handle;
+ info->timer = new QQuailTimer(info->handle, func, data);
+ info->timer->start(interval);
+
+ m_sources.insert(info->handle, info);
+
+ return info->handle;
}
static gboolean
qQuailTimeoutRemove(guint handle)
{
//qDebug() << "QQuailInputNotifier::qQuailTimeoutRemove";
- QQuailTimer *timer = eventLoop->m_timers.takeAt(handle);
+ qQuailSourceRemove(handle);
- if (timer == NULL)
- return false;
-
- delete timer;
- return true;
+ return 0;
}
static guint
@@ -181,21 +138,36 @@
gpointer userData)
{
//qDebug() << "QQuailInputNotifier::qQuailInputAdd";
- QQuailInputNotifier *notifier = new QQuailInputNotifier(fd, cond, func, userData);
- eventLoop->m_sources.append(notifier);
- return eventLoop->m_sources.lastIndexOf(notifier);
+ QQuailSourceInfo *info = new QQuailSourceInfo;
+
+ info->handle = nextSourceId++;
+
+ info->notifier = new QQuailInputNotifier(fd, cond, func, userData);
+
+ m_sources.insert(info->handle, info);
+
+ return info->handle;
}
static gboolean
qQuailSourceRemove(guint handle)
{
//qDebug() << "QQuailInputNotifier::qQuailSourceRemove";
- QQuailInputNotifier *notifier = eventLoop->m_sources.takeAt(handle);
+ QQuailSourceInfo *info;
- if (notifier == NULL)
+ info = m_sources.value(handle);
+
+ if (info == NULL)
return false;
- delete notifier;
+ m_sources.remove(handle);
+
+ if (info->timer != NULL)
+ delete info->timer;
+ else if (info->notifier != NULL)
+ delete info->notifier;
+
+ delete info;
return true;
}
@@ -231,5 +203,5 @@
PurpleEventLoopUiOps *
qQuailGetEventLoopUiOps(void)
{
- return &eventloop_ops;
+ return &eventloop_ops;
}
--- a/src/QuailEventLoop.h Fri Sep 13 14:11:27 2013 +0100
+++ b/src/QuailEventLoop.h Mon Sep 16 16:27:25 2013 +0100
@@ -24,81 +24,44 @@
#include <libpurple/eventloop.h>
-#include <QList>
#include <QTimer>
#include <QSocketNotifier>
/* http://harmattan-dev.nokia.com/docs/library/html/qt4/qabstracteventdispatcher.html */
-#include <QAbstractEventDispatcher>
-
-struct QuailTimerInfo {
- int id;
- int interval;
- QQuailTimer *quailTimer;
-};
+//#include <QAbstractEventDispatcher>
class QQuailTimer : public QTimer
{
- Q_OBJECT
+ Q_OBJECT
- public:
- QQuailTimer(GSourceFunc func, gpointer data);
+ public:
+ QQuailTimer(guint sourceId, GSourceFunc func, gpointer data);
- private slots:
- void update();
- void setHandle(guint newSourceId);
+ private slots:
+ void update();
- private:
- guint sourceId;
- GSourceFunc func;
- gpointer userData;
+ private:
+ guint sourceId;
+ GSourceFunc func;
+ gpointer userData;
};
class QQuailInputNotifier : public QObject
{
- Q_OBJECT
-
- public:
- QQuailInputNotifier(int fd, PurpleInputCondition cond,
- PurpleInputFunction func, gpointer userData);
- ~QQuailInputNotifier();
-
- private slots:
- void ioInvoke(int fd);
-
- private:
- PurpleInputCondition cond;
- PurpleInputFunction func;
- gpointer userData;
- QSocketNotifier *readNotifier, *writeNotifier;
-};
-
-class QuailEventLoop : QAbstractEventDispatcher
-{
Q_OBJECT
-public:
- QuailEventLoop(QObject *parent = 0);
- bool processEvents(QEventLoop::ProcessEventsFlags flags);
- bool hasPendingEvents();
-
- void registerSocketNotifier(QSocketNotifier *notifier);
- void unregisterSocketNotifier(QSocketNotifier *notifier);
+ public:
+ QQuailInputNotifier(int fd, PurpleInputCondition cond,
+ PurpleInputFunction func, gpointer userData);
+ ~QQuailInputNotifier();
- void registerTimer(int timerId, int interval, QObject *object);
- bool unregisterTimer(int timerId);
- bool unregisterTimers(QObject *object);
- QList<TimerInfo> registeredTimers(QObject *object) const;
+ private slots:
+ void ioInvoke(int fd);
- //void wakeUp();
- //void interrupt();
- //void flush();
-
- QList<QQuailInputNotifier*> m_sources;
- QList<QQuailTimer*> m_timers;
-
-private:
- //static QMap<guint, QQuailSourceInfo*> m_sources;
-
+ private:
+ PurpleInputCondition cond;
+ PurpleInputFunction func;
+ gpointer userData;
+ QSocketNotifier *readNotifier, *writeNotifier;
};
/**
--- a/src/QuailWinGlibEventLoop.cpp Fri Sep 13 14:11:27 2013 +0100
+++ b/src/QuailWinGlibEventLoop.cpp Mon Sep 16 16:27:25 2013 +0100
@@ -1,6 +1,243 @@
#include "QuailWinGlibEventLoop.h"
#include <QCoreApplication>
+#include <glib.h>
+
+struct GPollFDWithQSocketNotifier
+{
+ GPollFD pollfd;
+ QSocketNotifier *socketNotifier;
+};
+
+struct GSocketNotifierSource
+{
+ GSource source;
+ QList<GPollFDWithQSocketNotifier *> pollfds;
+};
+
+static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
+{
+ if (timeout)
+ *timeout = -1;
+ return false;
+}
+
+static gboolean socketNotifierSourceCheck(GSource *source)
+{
+ GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
+
+ bool pending = false;
+ for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
+ GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
+
+ if (p->pollfd.revents & G_IO_NVAL) {
+ // disable the invalid socket notifier
+ static const char *t[] = { "Read", "Write", "Exception" };
+ qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
+ p->pollfd.fd, t[int(p->socketNotifier->type())]);
+ // ### note, modifies src->pollfds!
+ p->socketNotifier->setEnabled(false);
+ }
+
+ pending = ((p->pollfd.revents & p->pollfd.events) != 0);
+ }
+
+ return pending;
+}
+
+static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+ QEvent event(QEvent::SockAct);
+
+ GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
+ for (int i = 0; i < src->pollfds.count(); ++i) {
+ GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
+
+ if ((p->pollfd.revents & p->pollfd.events) != 0)
+ QCoreApplication::sendEvent(p->socketNotifier, &event);
+ }
+
+ return true; // ??? don't remove, right?
+}
+
+static GSourceFuncs socketNotifierSourceFuncs = {
+ socketNotifierSourcePrepare,
+ socketNotifierSourceCheck,
+ socketNotifierSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct GTimerSource
+{
+ GSource source;
+ QTimerInfoList timerList;
+ QEventLoop::ProcessEventsFlags processEventsFlags;
+ bool runWithIdlePriority;
+};
+
+static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
+{
+ timespec tv = { 0l, 0l };
+ if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
+ *timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);
+ else
+ *timeout = -1;
+
+ return (*timeout == 0);
+}
+
+static gboolean timerSourceCheckHelper(GTimerSource *src)
+{
+ if (src->timerList.isEmpty()
+ || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
+ return false;
+
+ if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout)
+ return false;
+
+ return true;
+}
+
+static gboolean timerSourcePrepare(GSource *source, gint *timeout)
+{
+ gint dummy;
+ if (!timeout)
+ timeout = &dummy;
+
+ GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
+ if (src->runWithIdlePriority) {
+ if (timeout)
+ *timeout = -1;
+ return false;
+ }
+
+ return timerSourcePrepareHelper(src, timeout);
+}
+
+static gboolean timerSourceCheck(GSource *source)
+{
+ GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
+ if (src->runWithIdlePriority)
+ return false;
+ return timerSourceCheckHelper(src);
+}
+
+static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+ GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
+ if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
+ return true;
+ timerSource->runWithIdlePriority = true;
+ (void) timerSource->timerList.activateTimers();
+ return true; // ??? don't remove, right again?
+}
+
+static GSourceFuncs timerSourceFuncs = {
+ timerSourcePrepare,
+ timerSourceCheck,
+ timerSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct GIdleTimerSource
+{
+ GSource source;
+ GTimerSource *timerSource;
+};
+
+static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
+{
+ GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
+ GTimerSource *timerSource = idleTimerSource->timerSource;
+ if (!timerSource->runWithIdlePriority) {
+ // Yield to the normal priority timer source
+ if (timeout)
+ *timeout = -1;
+ return false;
+ }
+
+ return timerSourcePrepareHelper(timerSource, timeout);
+}
+
+static gboolean idleTimerSourceCheck(GSource *source)
+{
+ GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
+ GTimerSource *timerSource = idleTimerSource->timerSource;
+ if (!timerSource->runWithIdlePriority) {
+ // Yield to the normal priority timer source
+ return false;
+ }
+ return timerSourceCheckHelper(timerSource);
+}
+
+static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+ GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
+ (void) timerSourceDispatch(&timerSource->source, 0, 0);
+ return true;
+}
+
+static GSourceFuncs idleTimerSourceFuncs = {
+ idleTimerSourcePrepare,
+ idleTimerSourceCheck,
+ idleTimerSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct GPostEventSource
+{
+ GSource source;
+ QAtomicInt serialNumber;
+ int lastSerialNumber;
+ QuailEventDispatcherWinGlibPrivate *d;
+};
+
+static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
+{
+ QThreadData *data = QThreadData::current();
+ if (!data)
+ return false;
+
+ gint dummy;
+ if (!timeout)
+ timeout = &dummy;
+ const bool canWait = data->canWaitLocked();
+ *timeout = canWait ? -1 : 0;
+
+ GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
+ return (!canWait
+ || (source->serialNumber.load() != source->lastSerialNumber));
+}
+
+static gboolean postEventSourceCheck(GSource *source)
+{
+ return postEventSourcePrepare(source, 0);
+}
+
+static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
+{
+ GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
+ source->lastSerialNumber = source->serialNumber.load();
+ QCoreApplication::sendPostedEvents();
+ source->d->runTimersOnceWithNormalPriority();
+ return true; // i dunno, george...
+}
+
+static GSourceFuncs postEventSourceFuncs = {
+ postEventSourcePrepare,
+ postEventSourceCheck,
+ postEventSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
HINSTANCE qWinAppInst();
extern uint qGlobalPostedEventsCount();
@@ -265,11 +502,73 @@
}
}
-QuailEventDispatcherWinGliPrivateb::QuailEventDispatcherWinGlibPrivate()
- : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0),
+QuailEventDispatcherWinGlibPrivate::QuailEventDispatcherWinGlibPrivate(GMainContext *context = 0)
+ : mainContext(context), threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0),
serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0), wakeUps(0)
{
resolveTimerAPI();
+#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
+ if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
+ static QBasicMutex mutex;
+ QMutexLocker locker(&mutex);
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+ }
+#endif
+
+ if (mainContext) {
+ g_main_context_ref(mainContext);
+ } else {
+ QCoreApplication *app = QCoreApplication::instance();
+ if (app && QThread::currentThread() == app->thread()) {
+ mainContext = g_main_context_default();
+ g_main_context_ref(mainContext);
+ } else {
+ mainContext = g_main_context_new();
+ }
+ }
+
+#if GLIB_CHECK_VERSION (2, 22, 0)
+ g_main_context_push_thread_default (mainContext);
+#endif
+
+ // setup post event source
+ postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
+ sizeof(GPostEventSource)));
+ postEventSource->serialNumber.store(1);
+ postEventSource->d = this;
+ g_source_set_can_recurse(&postEventSource->source, true);
+ g_source_attach(&postEventSource->source, mainContext);
+
+ // setup socketNotifierSource
+ socketNotifierSource =
+ reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
+ sizeof(GSocketNotifierSource)));
+ (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
+ g_source_set_can_recurse(&socketNotifierSource->source, true);
+ g_source_attach(&socketNotifierSource->source, mainContext);
+
+ // setup normal and idle timer sources
+ timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
+ sizeof(GTimerSource)));
+ (void) new (&timerSource->timerList) QTimerInfoList();
+ timerSource->processEventsFlags = QEventLoop::AllEvents;
+ timerSource->runWithIdlePriority = false;
+ g_source_set_can_recurse(&timerSource->source, true);
+ g_source_attach(&timerSource->source, mainContext);
+
+ idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
+ sizeof(GIdleTimerSource)));
+ idleTimerSource->timerSource = timerSource;
+ g_source_set_can_recurse(&idleTimerSource->source, true);
+ g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
+ g_source_attach(&idleTimerSource->source, mainContext);
+
+}
+
+void QuailEventDispatcherWinGlibPrivate::runTimersOnceWithNormalPriority()
+{
+ timerSource->runWithIdlePriority = false;
}
QuailEventDispatcherWinGlibPrivate::~QuailEventDispatcherWinGlibPrivate()
--- a/src/QuailWinGlibEventLoop.h Fri Sep 13 14:11:27 2013 +0100
+++ b/src/QuailWinGlibEventLoop.h Mon Sep 16 16:27:25 2013 +0100
@@ -73,6 +73,7 @@
QSocketNotifier *obj;
int fd;
};
+
typedef QHash<int, QSockNot *> QSNDict;
struct WinTimerInfo { // internal timer info
@@ -101,7 +102,6 @@
{
Q_DECLARE_PUBLIC(QuailEventDispatcherWinGlib)
public:
- QuailEventDispatcherWinGlibPrivate();
QuailEventDispatcherWinGlibPrivate(GMainContext *context = 0);
~QuailEventDispatcherWinGlibPrivate();