├── src ├── .gitignore ├── win32_utils.h ├── eventdispatcher_libuv.pro ├── win32_utils.cpp ├── eventdispatcher_libuv.h ├── qt4compat.h ├── eventdispatcher_libuv_p.h ├── socknot_p.cpp ├── eventdispatcher_libuv_p.cpp ├── eventdispatcher_libuv.cpp └── timers_p.cpp ├── lib ├── .gitignore └── eventdispatcher_libuv.pri ├── .gitignore ├── .gitmodules ├── tests ├── eventdispatcher.h └── local.pri ├── .travis.yml ├── src-gui ├── eventdispatcher_libuv_qpa.pro ├── eventdispatcher_libuv_qpa.h └── eventdispatcher_libuv_qpa.cpp ├── LICENSE └── README.md /src/.gitignore: -------------------------------------------------------------------------------- 1 | pkgconfig 2 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | pkgconfig 2 | *.a 3 | *.prl 4 | *.pc 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.moc 4 | *.prl 5 | *.pro.user 6 | moc_*.cpp 7 | Makefile 8 | Makefile.* 9 | /tests/tst_* 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/qt_eventdispatcher_tests"] 2 | path = tests/qt_eventdispatcher_tests 3 | url = git://github.com/sjinks/qt_eventdispatcher_tests.git 4 | -------------------------------------------------------------------------------- /lib/eventdispatcher_libuv.pri: -------------------------------------------------------------------------------- 1 | HEADERS += $$PWD/eventdispatcher_libuv.h 2 | 3 | unix { 4 | CONFIG += link_pkgconfig 5 | PKGCONFIG += eventdispatcher_libuv 6 | } 7 | else:win32 { 8 | LIBS += -L$$PWD -leventdispatcher_libuv 9 | INCLUDEPATH += $$PWD 10 | DEPENDPATH += $$PWD 11 | } 12 | -------------------------------------------------------------------------------- /tests/eventdispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_H 2 | #define EVENTDISPATCHER_H 3 | 4 | #include "eventdispatcher_libuv.h" 5 | 6 | class EventDispatcher : public EventDispatcherLibUv { 7 | Q_OBJECT 8 | public: 9 | explicit EventDispatcher(QObject* parent = 0) : EventDispatcherLibUv(parent) {} 10 | }; 11 | 12 | #endif // EVENTDISPATCHER_H 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | - clang 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | dist: trusty 12 | sudo: required 13 | 14 | before_install: 15 | - sudo apt-get -qq update 16 | - sudo apt-get -qq -y install libuv-dev libqt4-dev libqt4-private-dev 17 | - git submodule update --init --recursive 18 | 19 | script: 20 | - ./build.sh 21 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libuv_qpa.pro: -------------------------------------------------------------------------------- 1 | QT += gui-private 2 | CONFIG += staticlib create_prl link_prl 3 | TEMPLATE = lib 4 | TARGET = eventdispatcher_libuv_qpa 5 | SOURCES = eventdispatcher_libuv_qpa.cpp 6 | HEADERS = eventdispatcher_libuv_qpa.h 7 | DESTDIR = ../lib 8 | 9 | LIBS += -L$$PWD/../lib -leventdispatcher_libuv 10 | INCLUDEPATH += $$PWD/../src 11 | DEPENDPATH += $$PWD/../src 12 | PRE_TARGETDEPS += $$PWD/../lib/libeventdispatcher_libuv.a 13 | -------------------------------------------------------------------------------- /tests/local.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD $$PWD/../src 2 | DEPENDPATH += $$PWD $$PWD/../src 3 | 4 | HEADERS += $$PWD/eventdispatcher.h 5 | 6 | CONFIG *= link_prl 7 | LIBS += -L$$OUT_PWD/$$DESTDIR/../lib -leventdispatcher_libuv 8 | 9 | unix|*-g++* { 10 | equals(QMAKE_PREFIX_STATICLIB, ""): QMAKE_PREFIX_STATICLIB = lib 11 | equals(QMAKE_EXTENSION_STATICLIB, ""): QMAKE_EXTENSION_STATICLIB = a 12 | 13 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/$${QMAKE_PREFIX_STATICLIB}eventdispatcher_libuv$${LIB_SUFFIX}.$${QMAKE_EXTENSION_STATICLIB} 14 | } 15 | else:win32 { 16 | PRE_TARGETDEPS *= $$OUT_PWD/$$DESTDIR/../lib/eventdispatcher_libuv$${LIB_SUFFIX}.lib 17 | } 18 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libuv_qpa.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBUV_QPA_H 2 | #define EVENTDISPATCHER_LIBUV_QPA_H 3 | 4 | #include "eventdispatcher_libuv.h" 5 | 6 | #if QT_VERSION < 0x050000 7 | # error This code requires at least Qt 5 8 | #endif 9 | 10 | class EventDispatcherLibUvQPA : public EventDispatcherLibUv { 11 | Q_OBJECT 12 | public: 13 | explicit EventDispatcherLibUvQPA(QObject* parent = 0); 14 | virtual ~EventDispatcherLibUvQPA(void); 15 | 16 | bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE; 17 | bool hasPendingEvents(void) Q_DECL_OVERRIDE; 18 | void flush(void) Q_DECL_OVERRIDE; 19 | 20 | private: 21 | Q_DISABLE_COPY(EventDispatcherLibUvQPA) 22 | }; 23 | 24 | #endif // EVENTDISPATCHER_LIBUV_QPA_H 25 | -------------------------------------------------------------------------------- /src-gui/eventdispatcher_libuv_qpa.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libuv_qpa.h" 5 | 6 | EventDispatcherLibUvQPA::EventDispatcherLibUvQPA(QObject* parent) 7 | : EventDispatcherLibUv(parent) 8 | { 9 | } 10 | 11 | EventDispatcherLibUvQPA::~EventDispatcherLibUvQPA(void) 12 | { 13 | } 14 | 15 | bool EventDispatcherLibUvQPA::processEvents(QEventLoop::ProcessEventsFlags flags) 16 | { 17 | bool sent_events = QWindowSystemInterface::sendWindowSystemEvents(flags); 18 | 19 | if (EventDispatcherLibUv::processEvents(flags)) { 20 | return true; 21 | } 22 | 23 | return sent_events; 24 | } 25 | 26 | bool EventDispatcherLibUvQPA::hasPendingEvents(void) 27 | { 28 | return EventDispatcherLibUv::hasPendingEvents() || QWindowSystemInterface::windowSystemEventsQueued(); 29 | } 30 | 31 | void EventDispatcherLibUvQPA::flush(void) 32 | { 33 | if (qApp) { 34 | qApp->sendPostedEvents(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/win32_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_UTILS_H 2 | #define WIN32_UTILS_H 3 | 4 | #include 5 | 6 | struct timezone 7 | { 8 | int tz_minuteswest; /* minutes W of Greenwich */ 9 | int tz_dsttime; /* type of dst correction */ 10 | }; 11 | 12 | Q_DECL_HIDDEN int gettimeofday(struct timeval* tv, struct timezone* tz); 13 | 14 | #ifndef timeradd 15 | #define timeradd(tvp, uvp, vvp) \ 16 | do { \ 17 | (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ 18 | (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ 19 | if ((vvp)->tv_usec >= 1000000) { \ 20 | ++(vvp)->tv_sec; \ 21 | (vvp)->tv_usec -= 1000000; \ 22 | } \ 23 | } while (0) 24 | #endif 25 | 26 | #ifndef timercmp 27 | #define timercmp(tvp, uvp, cmp) \ 28 | (((tvp)->tv_sec == (uvp)->tv_sec) ? ((tvp)->tv_usec cmp (uvp)->tv_usec) : ((tvp)->tv_sec cmp (uvp)->tv_sec)) 29 | #endif 30 | 31 | #endif // WIN32_UTILS_H 32 | -------------------------------------------------------------------------------- /src/eventdispatcher_libuv.pro: -------------------------------------------------------------------------------- 1 | QT -= gui 2 | TARGET = eventdispatcher_libuv 3 | TEMPLATE = lib 4 | DESTDIR = ../lib 5 | CONFIG += staticlib create_prl release 6 | HEADERS += eventdispatcher_libuv.h eventdispatcher_libuv_p.h 7 | SOURCES += eventdispatcher_libuv.cpp eventdispatcher_libuv_p.cpp timers_p.cpp socknot_p.cpp 8 | 9 | headers.files = eventdispatcher_libuv.h 10 | 11 | win32 { 12 | HEADERS += win32_utils.h 13 | SOURCES += win32_utils.cpp 14 | } 15 | 16 | unix { 17 | CONFIG += create_pc 18 | 19 | system('pkg-config --exists libuv') { 20 | CONFIG += link_pkgconfig 21 | PKGCONFIG += libuv 22 | } 23 | else { 24 | LIBS += -luv -lrt -ldl 25 | } 26 | 27 | target.path = /usr/lib 28 | headers.path = /usr/include 29 | 30 | QMAKE_PKGCONFIG_NAME = eventdispatcher_libuv 31 | QMAKE_PKGCONFIG_DESCRIPTION = "LibUv-based event dispatcher for Qt" 32 | QMAKE_PKGCONFIG_LIBDIR = $$target.path 33 | QMAKE_PKGCONFIG_INCDIR = $$headers.path 34 | QMAKE_PKGCONFIG_DESTDIR = pkgconfig 35 | } 36 | else { 37 | LIBS += -luv 38 | headers.path = $$DESTDIR 39 | } 40 | 41 | INSTALLS += target headers 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Vladimir Kolesnikov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/win32_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "win32_utils.h" 4 | 5 | #define DELTA_EPOCH_IN_MICROSECS Q_UINT64_C(11644473600000000) 6 | 7 | int gettimeofday(struct timeval* tv, struct timezone* tz) 8 | { 9 | if (tv) { 10 | FILETIME ft; 11 | quint64 tmpres = 0; 12 | 13 | GetSystemTimeAsFileTime(&ft); 14 | 15 | // The GetSystemTimeAsFileTime returns the number of 100 nanosecond 16 | // intervals since Jan 1, 1601 in a structure. Copy the high bits to 17 | // the 64 bit tmpres, shift it left by 32 then or in the low 32 bits; 18 | // convert to microseconds 19 | tmpres |= ft.dwHighDateTime; 20 | tmpres <<= 32; 21 | tmpres |= ft.dwLowDateTime; 22 | tmpres /= 10; 23 | 24 | // The Unix epoch starts on Jan 1 1970. Need to subtract the difference 25 | // in seconds from Jan 1 1601. 26 | tmpres -= DELTA_EPOCH_IN_MICROSECS; 27 | 28 | tv->tv_sec = (long int)(tmpres / 1000000UL); 29 | tv->tv_usec = (long int)(tmpres % 1000000UL); 30 | } 31 | 32 | if (tz) { 33 | TIME_ZONE_INFORMATION tzi; 34 | 35 | int res = GetTimeZoneInformation(&tzi); 36 | tz->tz_minuteswest = tzi.Bias + ((res == 2) ? tzi.DaylightBias : 0); 37 | tz->tz_dsttime = (res == 2) ? 1 : 0; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/eventdispatcher_libuv.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBUV_H 2 | #define EVENTDISPATCHER_LIBUV_H 3 | 4 | #include 5 | 6 | class EventDispatcherLibUvPrivate; 7 | 8 | class EventDispatcherLibUv : public QAbstractEventDispatcher { 9 | Q_OBJECT 10 | public: 11 | explicit EventDispatcherLibUv(QObject* parent = 0); 12 | virtual ~EventDispatcherLibUv(void); 13 | 14 | virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); 15 | virtual bool hasPendingEvents(void); 16 | 17 | virtual void registerSocketNotifier(QSocketNotifier* notifier); 18 | virtual void unregisterSocketNotifier(QSocketNotifier* notifier); 19 | 20 | virtual void registerTimer( 21 | int timerId, 22 | int interval, 23 | #if QT_VERSION >= 0x050000 24 | Qt::TimerType timerType, 25 | #endif 26 | QObject* object 27 | ); 28 | 29 | virtual bool unregisterTimer(int timerId); 30 | virtual bool unregisterTimers(QObject* object); 31 | virtual QList registeredTimers(QObject* object) const; 32 | #if QT_VERSION >= 0x050000 33 | virtual int remainingTime(int timerId); 34 | #endif 35 | 36 | #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 37 | virtual bool registerEventNotifier(QWinEventNotifier* notifier); 38 | virtual void unregisterEventNotifier(QWinEventNotifier* notifier); 39 | #endif 40 | 41 | virtual void wakeUp(void); 42 | virtual void interrupt(void); 43 | virtual void flush(void); 44 | 45 | protected: 46 | EventDispatcherLibUv(EventDispatcherLibUvPrivate& dd, QObject* parent = 0); 47 | 48 | private: 49 | Q_DISABLE_COPY(EventDispatcherLibUv) 50 | Q_DECLARE_PRIVATE(EventDispatcherLibUv) 51 | #if QT_VERSION >= 0x040600 52 | QScopedPointer d_ptr; 53 | #else 54 | EventDispatcherLibUvPrivate* d_ptr; 55 | #endif 56 | }; 57 | 58 | #endif // EVENTDISPATCHER_LIBUV_H 59 | -------------------------------------------------------------------------------- /src/qt4compat.h: -------------------------------------------------------------------------------- 1 | #ifndef QT4COMPAT_H 2 | #define QT4COMPAT_H 3 | 4 | #include 5 | #include 6 | 7 | #if !defined(Q_UNREACHABLE) && !defined(Q_ASSUME) 8 | # if defined(Q_CC_INTEL) || defined(Q_CC_MSVC) 9 | # define Q_UNREACHABLE() __assume(0) 10 | # define Q_ASSUME(s) __assume(s) 11 | # elif defined(Q_CC_CLANG) 12 | # define Q_UNREACHABLE() __builtin_unreachable() 13 | # elif defined(Q_CC_GNU) && defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 14 | # define Q_UNREACHABLE() __builtin_unreachable() 15 | # endif 16 | #endif 17 | 18 | #if !defined(Q_LIKELY) && !defined(Q_UNLIKELY) 19 | # ifdef __GNUC__ 20 | # define Q_LIKELY(expr) __builtin_expect(!!(expr), true) 21 | # define Q_UNLIKELY(expr) __builtin_expect(!!(expr), false) 22 | # endif 23 | #endif 24 | 25 | #ifndef Q_FUNC_INFO 26 | # if defined(_MSC_VER) 27 | # define Q_FUNC_INFO __FUNCSIG__ 28 | # elif defined(__GNUC__) || defined(_DIAB_TOOL) 29 | # define Q_FUNC_INFO __PRETTY_FUNCTION__ 30 | # endif 31 | #endif 32 | 33 | #ifndef Q_LIKELY 34 | # define Q_LIKELY(s) s 35 | #endif 36 | 37 | #ifndef Q_UNLIKELY 38 | # define Q_UNLIKELY(s) s 39 | #endif 40 | 41 | #ifndef Q_UNREACHABLE 42 | # define Q_UNREACHABLE() Q_ASSERT(false) 43 | #endif 44 | 45 | #ifndef Q_ASSUME 46 | # define Q_ASSUME(s) if (s) {} else { Q_UNREACHABLE(); } 47 | #endif 48 | 49 | #ifndef Q_DECL_HIDDEN 50 | # define Q_DECL_HIDDEN 51 | #endif 52 | 53 | #ifndef QT_STRINGIFY 54 | # define QT_STRINGIFY2(x) #x 55 | # define QT_STRINGIFY(x) QT_STRINGIFY2(x) 56 | #endif 57 | 58 | #ifndef Q_FUNC_INFO 59 | # if defined(Q_OS_SOLARIS) || defined(Q_CC_XLC) 60 | # define Q_FUNC_INFO __FILE__ "(line number unavailable)" 61 | # else 62 | # define Q_FUNC_INFO __FILE__ ":" QT_STRINGIFY(__LINE__) 63 | # endif 64 | #endif 65 | 66 | #ifndef Q_EMIT 67 | # define Q_EMIT emit 68 | #endif 69 | 70 | #if QT_VERSION < 0x050000 71 | namespace Qt { // Sorry 72 | enum TimerType { 73 | PreciseTimer, 74 | CoarseTimer, 75 | VeryCoarseTimer 76 | }; 77 | } 78 | 79 | typedef qptrdiff qintptr; 80 | #endif 81 | 82 | #endif // QT4COMPAT_H 83 | -------------------------------------------------------------------------------- /src/eventdispatcher_libuv_p.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBUV_P_H 2 | #define EVENTDISPATCHER_LIBUV_P_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if QT_VERSION >= 0x040400 11 | # include 12 | #endif 13 | 14 | #include "qt4compat.h" 15 | 16 | struct TimerInfo { 17 | QObject* object; 18 | uv_timer_t ev; 19 | struct timeval when; 20 | int timerId; 21 | int interval; 22 | Qt::TimerType type; 23 | }; 24 | 25 | struct ZeroTimer { 26 | QObject* object; 27 | bool active; 28 | }; 29 | 30 | Q_DECLARE_TYPEINFO(TimerInfo, Q_PRIMITIVE_TYPE); 31 | Q_DECLARE_TYPEINFO(ZeroTimer, Q_PRIMITIVE_TYPE); 32 | 33 | Q_DECL_HIDDEN uint64_t calculateNextTimeout(TimerInfo* info, const struct timeval& now); 34 | 35 | class EventDispatcherLibUv; 36 | 37 | class Q_DECL_HIDDEN EventDispatcherLibUvPrivate { 38 | public: 39 | EventDispatcherLibUvPrivate(EventDispatcherLibUv* const q); 40 | ~EventDispatcherLibUvPrivate(void); 41 | bool processEvents(QEventLoop::ProcessEventsFlags flags); 42 | bool processZeroTimers(void); 43 | void registerSocketNotifier(QSocketNotifier* notifier); 44 | void unregisterSocketNotifier(QSocketNotifier* notifier); 45 | void registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object); 46 | void registerZeroTimer(int timerId, QObject* object); 47 | bool unregisterTimer(int timerId); 48 | bool unregisterTimers(QObject* object); 49 | QList registeredTimers(QObject* object) const; 50 | int remainingTime(int timerId) const; 51 | 52 | typedef QHash SocketNotifierHash; 53 | typedef QHash TimerHash; 54 | typedef QPair, QEvent*> PendingEvent; 55 | typedef QList EventList; 56 | typedef QHash ZeroTimerHash; 57 | 58 | private: 59 | Q_DISABLE_COPY(EventDispatcherLibUvPrivate) 60 | Q_DECLARE_PUBLIC(EventDispatcherLibUv) 61 | EventDispatcherLibUv* const q_ptr; 62 | 63 | bool m_interrupt; 64 | uv_loop_t* m_base; 65 | uv_async_t m_wakeup; 66 | #if QT_VERSION >= 0x040400 67 | QAtomicInt m_wakeups; 68 | #endif 69 | SocketNotifierHash m_notifiers; 70 | TimerHash m_timers; 71 | EventList m_event_list; 72 | ZeroTimerHash m_zero_timers; 73 | bool m_awaken; 74 | 75 | static void socket_notifier_callback(uv_poll_t* w, int status, int events); 76 | static void timer_callback( 77 | uv_timer_t* w 78 | #if UV_VERSION_MAJOR < 1 79 | , int status 80 | #endif 81 | ); 82 | static void wake_up_handler( 83 | uv_async_t* w 84 | #if UV_VERSION_MAJOR < 1 85 | , int status 86 | #endif 87 | ); 88 | 89 | bool disableSocketNotifiers(bool disable); 90 | void killSocketNotifiers(void); 91 | bool disableTimers(bool disable); 92 | void killTimers(void); 93 | }; 94 | 95 | #endif // EVENTDISPATCHER_LIBUV_P_H 96 | -------------------------------------------------------------------------------- /src/socknot_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libuv_p.h" 5 | 6 | void EventDispatcherLibUvPrivate::registerSocketNotifier(QSocketNotifier* notifier) 7 | { 8 | int sockfd = notifier->socket(); 9 | int what; 10 | switch (notifier->type()) { 11 | case QSocketNotifier::Read: what = UV_READABLE; break; 12 | case QSocketNotifier::Write: what = UV_WRITABLE; break; 13 | case QSocketNotifier::Exception: /// FIXME 14 | return; 15 | 16 | default: 17 | Q_ASSERT(false); 18 | return; 19 | } 20 | 21 | uv_poll_t* data = new uv_poll_t; 22 | uv_poll_init(this->m_base, data, sockfd); 23 | data->data = notifier; 24 | uv_poll_start(data, what, &EventDispatcherLibUvPrivate::socket_notifier_callback); 25 | 26 | this->m_notifiers.insert(notifier, data); 27 | } 28 | 29 | void EventDispatcherLibUvPrivate::unregisterSocketNotifier(QSocketNotifier* notifier) 30 | { 31 | SocketNotifierHash::Iterator it = this->m_notifiers.find(notifier); 32 | if (it != this->m_notifiers.end()) { 33 | uv_poll_t* data = it.value(); 34 | Q_ASSERT(data->data == notifier); 35 | 36 | uv_poll_stop(data); 37 | delete data; 38 | it = this->m_notifiers.erase(it); 39 | } 40 | } 41 | 42 | void EventDispatcherLibUvPrivate::socket_notifier_callback(uv_poll_t* w, int status, int events) 43 | { 44 | Q_UNUSED(status) 45 | 46 | EventDispatcherLibUvPrivate* disp = static_cast(w->loop->data); 47 | QSocketNotifier* notifier = static_cast(w->data); 48 | QSocketNotifier::Type type = notifier->type(); 49 | 50 | if ((QSocketNotifier::Read == type && (events & UV_READABLE)) || (QSocketNotifier::Write == type && (events & UV_WRITABLE))) { 51 | PendingEvent event(notifier, new QEvent(QEvent::SockAct)); 52 | disp->m_event_list.append(event); 53 | } 54 | } 55 | 56 | bool EventDispatcherLibUvPrivate::disableSocketNotifiers(bool disable) 57 | { 58 | SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 59 | while (it != this->m_notifiers.end()) { 60 | uv_poll_t* data = it.value(); 61 | if (disable) { 62 | uv_poll_stop(data); 63 | } 64 | else { 65 | QSocketNotifier* notifier = it.key(); 66 | int what; 67 | switch (notifier->type()) { 68 | case QSocketNotifier::Read: what = UV_READABLE; break; 69 | case QSocketNotifier::Write: what = UV_WRITABLE; break; 70 | case QSocketNotifier::Exception: /// FIXME 71 | continue; 72 | 73 | default: 74 | Q_ASSERT(false); 75 | continue; 76 | } 77 | 78 | uv_poll_start(data, what, &EventDispatcherLibUvPrivate::socket_notifier_callback); 79 | } 80 | 81 | ++it; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | void EventDispatcherLibUvPrivate::killSocketNotifiers(void) 88 | { 89 | if (!this->m_notifiers.isEmpty()) { 90 | SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 91 | while (it != this->m_notifiers.end()) { 92 | uv_poll_t* data = it.value(); 93 | uv_poll_stop(data); 94 | delete data; 95 | ++it; 96 | } 97 | 98 | this->m_notifiers.clear(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qt_eventdispatcher_libuv [![Build Status](https://secure.travis-ci.org/sjinks/qt_eventdispatcher_libuv.png)](http://travis-ci.org/sjinks/qt_eventdispatcher_libuv) 2 | 3 | libuv-based event dispatcher for Qt 4 | 5 | 6 | ## Features 7 | * very fast :-) 8 | * compatible with Qt 4 and Qt 5 9 | * does not use any private Qt headers 10 | * passes Qt 4 and Qt 5 event dispatcher, event loop, timer and socket notifier tests 11 | 12 | 13 | ## Unsupported Features 14 | * `QSocketNotifier::Exception` (libuv offers no support for this) 15 | * Qt 5/Windows only: `QWinEventNotifier` is not supported (`registerEventNotifier()` and `unregisterEventNotifier()` functions are currently implemented as stubs) 16 | 17 | 18 | ## Requirements 19 | * libuv >= 0.10 20 | * Qt >= 4.2.1 (tests from tests-qt4 were run only on Qt 4.8.x, 4.5.4, 4.3.0, 4.2.1) 21 | 22 | 23 | ## Build 24 | 25 | ``` 26 | cd src 27 | qmake 28 | make 29 | ``` 30 | 31 | Replace `make` with `nmake` if your are using Microsoft Visual C++. 32 | 33 | The above commands will generate the static library and `.prl` file in `../lib` directory. 34 | 35 | 36 | ## Install 37 | 38 | After completing Build step run 39 | 40 | *NIX: 41 | ``` 42 | sudo make install 43 | ``` 44 | 45 | Windows: 46 | ``` 47 | nmake install 48 | ``` 49 | 50 | For Windows this will copy `eventdispatcher_libuv.h` to `../lib` directory. 51 | For *NIX this will install eventdispatcher_libuv.h to `/usr/include`, `libeventdispatcher_libuv.a` and `libeventdispatcher_libuv.prl` to `/usr/lib`, `eventdispatcher_libuv.pc` to `/usr/lib/pkgconfig`. 52 | 53 | 54 | ## Usage (Qt 4) 55 | 56 | Simply include the header file and instantiate the dispatcher in `main()` 57 | before creating the Qt application object. 58 | 59 | ```c++ 60 | #include "eventdispatcher_libuv.h" 61 | 62 | int main(int argc, char** argv) 63 | { 64 | EventDispatcherLibUv dispatcher; 65 | QCoreApplication app(argc, argv); 66 | 67 | // ... 68 | 69 | return app.exec(); 70 | } 71 | ``` 72 | 73 | And add these lines to the .pro file: 74 | 75 | ``` 76 | unix { 77 | CONFIG += link_pkgconfig 78 | PKGCONFIG += eventdispatcher_libuv 79 | } 80 | else:win32 { 81 | include(/path/to/qt_eventdispatcher_libuv/lib/eventdispatcher_libuv.pri) 82 | } 83 | ``` 84 | 85 | or 86 | 87 | ``` 88 | HEADERS += /path/to/eventdispatcher_libuv.h 89 | LIBS += -L/path/to/library -leventdispatcher_libuv 90 | ``` 91 | 92 | 93 | ## Usage (Qt 5) 94 | 95 | Simply include the header file and instantiate the dispatcher in `main()` 96 | before creating the Qt application object. 97 | 98 | ```c++ 99 | #include "eventdispatcher_libuv.h" 100 | 101 | int main(int argc, char** argv) 102 | { 103 | QCoreApplication::setEventDispatcher(new EventDispatcherLibUv); 104 | QCoreApplication app(argc, argv); 105 | 106 | // ... 107 | 108 | return app.exec(); 109 | } 110 | ``` 111 | 112 | And add these lines to the .pro file: 113 | 114 | ``` 115 | unix { 116 | CONFIG += link_pkgconfig 117 | PKGCONFIG += eventdispatcher_libuv 118 | } 119 | else:win32 { 120 | include(/path/to/qt_eventdispatcher_libev/lib/eventdispatcher_libuv.pri) 121 | } 122 | ``` 123 | 124 | or 125 | 126 | ``` 127 | HEADERS += /path/to/eventdispatcher_libuv.h 128 | LIBS += -L/path/to/library -leventdispatcher_libuv 129 | ``` 130 | 131 | Qt 5 allows to specify a custom event dispatcher for the thread: 132 | 133 | ```c++ 134 | QThread* thr = new QThread; 135 | thr->setEventDispatcher(new EventDispatcherLibUv); 136 | ``` 137 | -------------------------------------------------------------------------------- /src/eventdispatcher_libuv_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "eventdispatcher_libuv.h" 3 | #include "eventdispatcher_libuv_p.h" 4 | 5 | #ifdef WIN32 6 | # include "win32_utils.h" 7 | #endif 8 | 9 | EventDispatcherLibUvPrivate::EventDispatcherLibUvPrivate(EventDispatcherLibUv* const q) 10 | : q_ptr(q), m_interrupt(false), m_base(0), m_wakeup(), 11 | #if QT_VERSION >= 0x040400 12 | m_wakeups(), 13 | #endif 14 | m_notifiers(), m_timers(), m_event_list(), m_zero_timers(), m_awaken(false) 15 | { 16 | #if UV_VERSION_MAJOR < 1 17 | this->m_base = uv_loop_new(); 18 | if (!this->m_base) { 19 | qFatal("%s: failed to initialize the event loop", Q_FUNC_INFO); 20 | } 21 | #else 22 | this->m_base = new uv_loop_t; 23 | uv_loop_init(this->m_base); 24 | #endif 25 | 26 | this->m_base->data = this; 27 | 28 | uv_async_init(this->m_base, &this->m_wakeup, EventDispatcherLibUvPrivate::wake_up_handler); 29 | } 30 | 31 | EventDispatcherLibUvPrivate::~EventDispatcherLibUvPrivate(void) 32 | { 33 | if (this->m_base) { 34 | // uv_close(&this->m_wakeup, 0); 35 | 36 | this->killTimers(); 37 | this->killSocketNotifiers(); 38 | 39 | #if UV_VERSION_MAJOR < 1 40 | uv_loop_delete(this->m_base); 41 | #else 42 | uv_loop_close(this->m_base); 43 | delete this->m_base; 44 | #endif 45 | 46 | this->m_base = 0; 47 | } 48 | } 49 | 50 | bool EventDispatcherLibUvPrivate::processEvents(QEventLoop::ProcessEventsFlags flags) 51 | { 52 | Q_Q(EventDispatcherLibUv); 53 | 54 | const bool exclude_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers); 55 | const bool exclude_timers = (flags & QEventLoop::X11ExcludeTimers); 56 | 57 | exclude_notifiers && this->disableSocketNotifiers(true); 58 | exclude_timers && this->disableTimers(true); 59 | 60 | this->m_interrupt = false; 61 | this->m_awaken = false; 62 | 63 | bool result = q->hasPendingEvents(); 64 | 65 | Q_EMIT q->awake(); 66 | #if QT_VERSION < 0x040500 67 | QCoreApplication::sendPostedEvents(0, (flags & QEventLoop::DeferredDeletion) ? -1 : 0); 68 | #else 69 | QCoreApplication::sendPostedEvents(); 70 | #endif 71 | 72 | bool can_wait = !this->m_interrupt && (flags & QEventLoop::WaitForMoreEvents) && !result; 73 | uv_run_mode f = UV_RUN_NOWAIT; 74 | 75 | if (!this->m_interrupt) { 76 | if (!exclude_timers && this->m_zero_timers.size() > 0) { 77 | result |= this->processZeroTimers(); 78 | if (result) { 79 | can_wait = false; 80 | } 81 | } 82 | 83 | if (can_wait) { 84 | Q_EMIT q->aboutToBlock(); 85 | f = UV_RUN_ONCE; 86 | } 87 | 88 | // Work around a bug when libev returns from ev_loop(loop, EVLOOP_ONESHOT) without processing any events 89 | // do { 90 | uv_run(this->m_base, f); 91 | // } while (can_wait && !this->m_awaken && !this->m_event_list.size()); 92 | 93 | #if QT_VERSION >= 0x040800 94 | EventList list; 95 | this->m_event_list.swap(list); 96 | #else 97 | EventList list(this->m_event_list); 98 | this->m_event_list.clear(); 99 | #endif 100 | 101 | result |= (list.size() > 0) | this->m_awaken; 102 | 103 | for (int i=0; itype() == QEvent::Timer) { 117 | QTimerEvent* te = static_cast(e.second); 118 | TimerHash::Iterator tit = this->m_timers.find(te->timerId()); 119 | if (tit != this->m_timers.end()) { 120 | TimerInfo* info = tit.value(); 121 | 122 | uv_timer_t* tmp = &info->ev; 123 | if (!uv_is_active(reinterpret_cast(tmp))) { // false in tst_QTimer::restartedTimerFiresTooSoon() 124 | uint64_t delta = calculateNextTimeout(info, now); 125 | uv_timer_start(tmp, &EventDispatcherLibUvPrivate::timer_callback, delta, 0); 126 | } 127 | } 128 | } 129 | 130 | delete e.second; 131 | } 132 | } 133 | 134 | exclude_notifiers && this->disableSocketNotifiers(false); 135 | exclude_timers && this->disableTimers(false); 136 | 137 | return result; 138 | } 139 | 140 | bool EventDispatcherLibUvPrivate::processZeroTimers(void) 141 | { 142 | bool result = false; 143 | QList ids = this->m_zero_timers.keys(); 144 | 145 | for (int i=0; im_zero_timers.find(tid); 148 | if (it != this->m_zero_timers.end()) { 149 | ZeroTimer& data = it.value(); 150 | if (data.active) { 151 | data.active = false; 152 | 153 | QTimerEvent event(tid); 154 | QCoreApplication::sendEvent(data.object, &event); 155 | result = true; 156 | 157 | it = this->m_zero_timers.find(tid); 158 | if (it != this->m_zero_timers.end()) { 159 | ZeroTimer& data = it.value(); 160 | if (!data.active) { 161 | data.active = true; 162 | } 163 | } 164 | } 165 | } 166 | } 167 | 168 | return result; 169 | } 170 | 171 | void EventDispatcherLibUvPrivate::wake_up_handler( 172 | uv_async_t* w 173 | #if UV_VERSION_MAJOR < 1 174 | , int 175 | #endif 176 | ) 177 | { 178 | EventDispatcherLibUvPrivate* disp = static_cast(w->loop->data); 179 | disp->m_awaken = true; 180 | 181 | #if QT_VERSION >= 0x040400 182 | if (!disp->m_wakeups.testAndSetRelease(1, 0)) { 183 | qCritical("%s: internal error, wakeUps.testAndSetRelease(1, 0) failed!", Q_FUNC_INFO); 184 | } 185 | #endif 186 | } 187 | -------------------------------------------------------------------------------- /src/eventdispatcher_libuv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libuv.h" 5 | #include "eventdispatcher_libuv_p.h" 6 | 7 | EventDispatcherLibUv::EventDispatcherLibUv(QObject* parent) 8 | : QAbstractEventDispatcher(parent), d_ptr(new EventDispatcherLibUvPrivate(this)) 9 | { 10 | } 11 | 12 | EventDispatcherLibUv::~EventDispatcherLibUv(void) 13 | { 14 | #if QT_VERSION < 0x040600 15 | delete this->d_ptr; 16 | this->d_ptr = 0; 17 | #endif 18 | } 19 | 20 | bool EventDispatcherLibUv::processEvents(QEventLoop::ProcessEventsFlags flags) 21 | { 22 | Q_D(EventDispatcherLibUv); 23 | return d->processEvents(flags); 24 | } 25 | 26 | bool EventDispatcherLibUv::hasPendingEvents(void) 27 | { 28 | extern uint qGlobalPostedEventsCount(); 29 | return qGlobalPostedEventsCount() > 0; 30 | } 31 | 32 | void EventDispatcherLibUv::registerSocketNotifier(QSocketNotifier* notifier) 33 | { 34 | #ifndef QT_NO_DEBUG 35 | if (notifier->socket() < 0) { 36 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 37 | return; 38 | } 39 | 40 | if (notifier->thread() != thread() || thread() != QThread::currentThread()) { 41 | qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); 42 | return; 43 | } 44 | #endif 45 | 46 | if (notifier->type() == QSocketNotifier::Exception) { 47 | return; 48 | } 49 | 50 | Q_D(EventDispatcherLibUv); 51 | d->registerSocketNotifier(notifier); 52 | } 53 | 54 | void EventDispatcherLibUv::unregisterSocketNotifier(QSocketNotifier* notifier) 55 | { 56 | #ifndef QT_NO_DEBUG 57 | if (notifier->socket() < 0) { 58 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 59 | return; 60 | } 61 | 62 | if (notifier->thread() != thread() || thread() != QThread::currentThread()) { 63 | qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); 64 | return; 65 | } 66 | #endif 67 | 68 | // Short circuit, we do not support QSocketNotifier::Exception 69 | if (notifier->type() == QSocketNotifier::Exception) { 70 | return; 71 | } 72 | 73 | Q_D(EventDispatcherLibUv); 74 | d->unregisterSocketNotifier(notifier); 75 | } 76 | 77 | void EventDispatcherLibUv::registerTimer( 78 | int timerId, 79 | int interval, 80 | #if QT_VERSION >= 0x050000 81 | Qt::TimerType timerType, 82 | #endif 83 | QObject* object 84 | ) 85 | { 86 | #ifndef QT_NO_DEBUG 87 | if (timerId < 1 || interval < 0 || !object) { 88 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 89 | return; 90 | } 91 | 92 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 93 | qWarning("%s: timers cannot be started from another thread", Q_FUNC_INFO); 94 | return; 95 | } 96 | #endif 97 | 98 | Qt::TimerType type; 99 | #if QT_VERSION >= 0x050000 100 | type = timerType; 101 | #else 102 | type = Qt::CoarseTimer; 103 | #endif 104 | 105 | Q_D(EventDispatcherLibUv); 106 | if (interval) { 107 | d->registerTimer(timerId, interval, type, object); 108 | } 109 | else { 110 | d->registerZeroTimer(timerId, object); 111 | } 112 | } 113 | 114 | bool EventDispatcherLibUv::unregisterTimer(int timerId) 115 | { 116 | #ifndef QT_NO_DEBUG 117 | if (timerId < 1) { 118 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 119 | return false; 120 | } 121 | 122 | if (this->thread() != QThread::currentThread()) { 123 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 124 | return false; 125 | } 126 | #endif 127 | 128 | Q_D(EventDispatcherLibUv); 129 | return d->unregisterTimer(timerId); 130 | } 131 | 132 | bool EventDispatcherLibUv::unregisterTimers(QObject* object) 133 | { 134 | #ifndef QT_NO_DEBUG 135 | if (!object) { 136 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 137 | return false; 138 | } 139 | 140 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 141 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 142 | return false; 143 | } 144 | #endif 145 | 146 | Q_D(EventDispatcherLibUv); 147 | return d->unregisterTimers(object); 148 | } 149 | 150 | QList EventDispatcherLibUv::registeredTimers(QObject* object) const 151 | { 152 | if (!object) { 153 | qWarning("%s: invalid argument", Q_FUNC_INFO); 154 | return QList(); 155 | } 156 | 157 | Q_D(const EventDispatcherLibUv); 158 | return d->registeredTimers(object); 159 | } 160 | 161 | #if QT_VERSION >= 0x050000 162 | int EventDispatcherLibUv::remainingTime(int timerId) 163 | { 164 | Q_D(const EventDispatcherLibUv); 165 | return d->remainingTime(timerId); 166 | } 167 | #endif 168 | 169 | #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 170 | bool EventDispatcherLibUv::registerEventNotifier(QWinEventNotifier* notifier) 171 | { 172 | Q_UNUSED(notifier) 173 | Q_UNIMPLEMENTED(); 174 | return false; 175 | } 176 | 177 | void EventDispatcherLibUv::unregisterEventNotifier(QWinEventNotifier* notifier) 178 | { 179 | Q_UNUSED(notifier) 180 | Q_UNIMPLEMENTED(); 181 | } 182 | #endif 183 | 184 | void EventDispatcherLibUv::wakeUp(void) 185 | { 186 | Q_D(EventDispatcherLibUv); 187 | 188 | #if QT_VERSION >= 0x040400 189 | if (d->m_wakeups.testAndSetAcquire(0, 1)) 190 | #endif 191 | { 192 | uv_async_send(&d->m_wakeup); 193 | } 194 | } 195 | 196 | void EventDispatcherLibUv::interrupt(void) 197 | { 198 | Q_D(EventDispatcherLibUv); 199 | d->m_interrupt = true; 200 | this->wakeUp(); 201 | } 202 | 203 | void EventDispatcherLibUv::flush(void) 204 | { 205 | } 206 | 207 | EventDispatcherLibUv::EventDispatcherLibUv(EventDispatcherLibUvPrivate& dd, QObject* parent) 208 | : QAbstractEventDispatcher(parent), d_ptr(&dd) 209 | { 210 | } 211 | -------------------------------------------------------------------------------- /src/timers_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libuv_p.h" 5 | 6 | #ifdef WIN32 7 | # include "win32_utils.h" 8 | #endif 9 | 10 | namespace { 11 | 12 | static void calculateCoarseTimerTimeout(TimerInfo* info, const struct timeval& now, struct timeval& when) 13 | { 14 | Q_ASSERT(info->interval > 20); 15 | // The coarse timer works like this: 16 | // - interval under 40 ms: round to even 17 | // - between 40 and 99 ms: round to multiple of 4 18 | // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5% 19 | // 20 | // We try to wake up at the following second-fraction, in order of preference: 21 | // 0 ms 22 | // 500 ms 23 | // 250 ms or 750 ms 24 | // 200, 400, 600, 800 ms 25 | // other multiples of 100 26 | // other multiples of 50 27 | // other multiples of 25 28 | // 29 | // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups. 30 | 31 | int interval = info->interval; 32 | int msec = static_cast(info->when.tv_usec / 1000); 33 | int max_rounding = interval / 20; // 5% 34 | when = info->when; 35 | 36 | if (interval < 100 && (interval % 25) != 0) { 37 | if (interval < 50) { 38 | uint round_up = ((msec % 50) >= 25) ? 1 : 0; 39 | msec = ((msec >> 1) | round_up) << 1; 40 | } 41 | else { 42 | uint round_up = ((msec % 100) >= 50) ? 1 : 0; 43 | msec = ((msec >> 2) | round_up) << 2; 44 | } 45 | } 46 | else { 47 | int min = qMax(0, msec - max_rounding); 48 | int max = qMin(1000, msec + max_rounding); 49 | 50 | bool done = false; 51 | 52 | // take any round-to-the-second timeout 53 | if (min == 0) { 54 | msec = 0; 55 | done = true; 56 | } 57 | else if (max == 1000) { 58 | msec = 1000; 59 | done = true; 60 | } 61 | 62 | if (!done) { 63 | int boundary; 64 | 65 | // if the interval is a multiple of 500 ms and > 5000 ms, always round towards a round-to-the-second 66 | // if the interval is a multiple of 500 ms, round towards the nearest multiple of 500 ms 67 | if ((interval % 500) == 0) { 68 | if (interval >= 5000) { 69 | msec = msec >= 500 ? max : min; 70 | done = true; 71 | } 72 | else { 73 | boundary = 500; 74 | } 75 | } 76 | else if ((interval % 50) == 0) { 77 | // same for multiples of 250, 200, 100, 50 78 | uint tmp = interval / 50; 79 | if ((tmp % 4) == 0) { 80 | boundary = 200; 81 | } 82 | else if ((tmp % 2) == 0) { 83 | boundary = 100; 84 | } 85 | else if ((tmp % 5) == 0) { 86 | boundary = 250; 87 | } 88 | else { 89 | boundary = 50; 90 | } 91 | } 92 | else { 93 | boundary = 25; 94 | } 95 | 96 | if (!done) { 97 | int base = (msec / boundary) * boundary; 98 | int middle = base + boundary / 2; 99 | msec = (msec < middle) ? qMax(base, min) : qMin(base + boundary, max); 100 | } 101 | } 102 | } 103 | 104 | if (msec == 1000) { 105 | ++when.tv_sec; 106 | when.tv_usec = 0; 107 | } 108 | else { 109 | when.tv_usec = msec * 1000; 110 | } 111 | 112 | if (timercmp(&when, &now, <)) { 113 | when.tv_sec += interval / 1000; 114 | when.tv_usec += (interval % 1000) * 1000; 115 | if (when.tv_usec > 999999) { 116 | ++when.tv_sec; 117 | when.tv_usec -= 1000000; 118 | } 119 | } 120 | 121 | Q_ASSERT(timercmp(&now, &when, <=)); 122 | } 123 | } 124 | 125 | uint64_t calculateNextTimeout(TimerInfo* info, const struct timeval& now) 126 | { 127 | struct timeval tv_interval; 128 | struct timeval when; 129 | tv_interval.tv_sec = info->interval / 1000; 130 | tv_interval.tv_usec = (info->interval % 1000) * 1000; 131 | 132 | if (info->interval) { 133 | qlonglong tnow = (qlonglong(now.tv_sec) * 1000) + (now.tv_usec / 1000); 134 | qlonglong twhen = (qlonglong(info->when.tv_sec) * 1000) + (info->when.tv_usec / 1000); 135 | 136 | if ((info->interval < 1000 && twhen - tnow > 1500) || (info->interval >= 1000 && twhen - tnow > 1.2*info->interval)) { 137 | info->when = now; 138 | } 139 | } 140 | 141 | if (Qt::VeryCoarseTimer == info->type) { 142 | if (info->when.tv_usec >= 500000) { 143 | ++info->when.tv_sec; 144 | } 145 | 146 | info->when.tv_usec = 0; 147 | info->when.tv_sec += info->interval / 1000; 148 | if (info->when.tv_sec <= now.tv_sec) { 149 | info->when.tv_sec = now.tv_sec + info->interval / 1000; 150 | } 151 | 152 | when = info->when; 153 | } 154 | else if (Qt::PreciseTimer == info->type) { 155 | if (info->interval) { 156 | timeradd(&info->when, &tv_interval, &info->when); 157 | if (timercmp(&info->when, &now, <)) { 158 | timeradd(&now, &tv_interval, &info->when); 159 | } 160 | 161 | when = info->when; 162 | } 163 | else { 164 | when = now; 165 | } 166 | } 167 | else { 168 | timeradd(&info->when, &tv_interval, &info->when); 169 | if (timercmp(&info->when, &now, <)) { 170 | timeradd(&now, &tv_interval, &info->when); 171 | } 172 | 173 | calculateCoarseTimerTimeout(info, now, when); 174 | } 175 | 176 | return static_cast(when.tv_sec - now.tv_sec)*1000 + static_cast(when.tv_usec - now.tv_usec)/1000; 177 | } 178 | 179 | 180 | void EventDispatcherLibUvPrivate::registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object) 181 | { 182 | Q_ASSERT(interval > 0); 183 | 184 | struct timeval now; 185 | gettimeofday(&now, 0); 186 | 187 | TimerInfo* info = new TimerInfo; 188 | info->timerId = timerId; 189 | info->interval = interval; 190 | info->type = type; 191 | info->object = object; 192 | info->when = now; // calculateNextTimeout() will take care of info->when 193 | 194 | if (Qt::CoarseTimer == type) { 195 | if (interval >= 20000) { 196 | info->type = Qt::VeryCoarseTimer; 197 | } 198 | else if (interval <= 20) { 199 | info->type = Qt::PreciseTimer; 200 | } 201 | } 202 | 203 | uint64_t delta = calculateNextTimeout(info, now); 204 | 205 | uv_timer_t* tmp = &info->ev; 206 | uv_timer_init(this->m_base, tmp); 207 | info->ev.data = info; 208 | uv_timer_start(tmp, EventDispatcherLibUvPrivate::timer_callback, delta, 0); 209 | this->m_timers.insert(timerId, info); 210 | } 211 | 212 | void EventDispatcherLibUvPrivate::registerZeroTimer(int timerId, QObject* object) 213 | { 214 | ZeroTimer data; 215 | data.object = object; 216 | data.active = true; 217 | this->m_zero_timers.insert(timerId, data); 218 | } 219 | 220 | bool EventDispatcherLibUvPrivate::unregisterTimer(int timerId) 221 | { 222 | TimerHash::Iterator it = this->m_timers.find(timerId); 223 | if (it != this->m_timers.end()) { 224 | TimerInfo* info = it.value(); 225 | uv_timer_stop(&info->ev); 226 | delete info; 227 | this->m_timers.erase(it); 228 | return true; 229 | } 230 | 231 | return this->m_zero_timers.remove(timerId) > 0; 232 | } 233 | 234 | bool EventDispatcherLibUvPrivate::unregisterTimers(QObject* object) 235 | { 236 | bool result = false; 237 | TimerHash::Iterator it = this->m_timers.begin(); 238 | while (it != this->m_timers.end()) { 239 | TimerInfo* info = it.value(); 240 | 241 | if (object == info->object) { 242 | result = true; 243 | uv_timer_stop(&info->ev); 244 | delete info; 245 | it = this->m_timers.erase(it); 246 | } 247 | else { 248 | ++it; 249 | } 250 | } 251 | 252 | ZeroTimerHash::Iterator zit = this->m_zero_timers.begin(); 253 | while (zit != this->m_zero_timers.end()) { 254 | ZeroTimer& data = zit.value(); 255 | if (object == data.object) { 256 | result = true; 257 | zit = this->m_zero_timers.erase(zit); 258 | } 259 | else { 260 | ++zit; 261 | } 262 | } 263 | 264 | return result; 265 | } 266 | 267 | QList EventDispatcherLibUvPrivate::registeredTimers(QObject* object) const 268 | { 269 | QList res; 270 | 271 | TimerHash::ConstIterator it = this->m_timers.constBegin(); 272 | while (it != this->m_timers.constEnd()) { 273 | TimerInfo* info = it.value(); 274 | if (object == info->object) { 275 | #if QT_VERSION < 0x050000 276 | QAbstractEventDispatcher::TimerInfo ti(it.key(), info->interval); 277 | #else 278 | QAbstractEventDispatcher::TimerInfo ti(it.key(), info->interval, info->type); 279 | #endif 280 | res.append(ti); 281 | } 282 | 283 | ++it; 284 | } 285 | 286 | ZeroTimerHash::ConstIterator zit = this->m_zero_timers.constBegin(); 287 | while (zit != this->m_zero_timers.constEnd()) { 288 | const ZeroTimer& data = zit.value(); 289 | if (object == data.object) { 290 | #if QT_VERSION < 0x050000 291 | QAbstractEventDispatcher::TimerInfo ti(it.key(), 0); 292 | #else 293 | QAbstractEventDispatcher::TimerInfo ti(it.key(), 0, Qt::PreciseTimer); 294 | #endif 295 | res.append(ti); 296 | } 297 | 298 | ++zit; 299 | } 300 | 301 | return res; 302 | } 303 | 304 | int EventDispatcherLibUvPrivate::remainingTime(int timerId) const 305 | { 306 | TimerHash::ConstIterator it = this->m_timers.find(timerId); 307 | if (it != this->m_timers.end()) { 308 | const TimerInfo* info = it.value(); 309 | 310 | const uv_timer_t* tmp = &info->ev; 311 | if (uv_is_active(reinterpret_cast(tmp))) { 312 | uint64_t now = uv_now(this->m_base); 313 | uint64_t when = static_cast(info->when.tv_sec)*1000 + static_cast(info->when.tv_usec)/1000; 314 | 315 | if (now > when) { 316 | return 0; 317 | } 318 | 319 | return static_cast((when - now) * 1000); 320 | } 321 | } 322 | 323 | // For zero timers we return -1 as well 324 | 325 | return -1; 326 | } 327 | 328 | void EventDispatcherLibUvPrivate::timer_callback( 329 | uv_timer_t* w 330 | #if UV_VERSION_MAJOR < 1 331 | , int 332 | #endif 333 | ) 334 | { 335 | EventDispatcherLibUvPrivate* self = static_cast(w->loop->data); 336 | TimerInfo* info = static_cast(w->data); 337 | 338 | // Timer can be reactivated only after its callback finishes; processEvents() will take care of this 339 | PendingEvent event(info->object, new QTimerEvent(info->timerId)); 340 | self->m_event_list.append(event); 341 | } 342 | 343 | bool EventDispatcherLibUvPrivate::disableTimers(bool disable) 344 | { 345 | struct timeval now; 346 | if (!disable) { 347 | gettimeofday(&now, 0); 348 | } 349 | 350 | TimerHash::Iterator it = this->m_timers.begin(); 351 | while (it != this->m_timers.end()) { 352 | TimerInfo* info = it.value(); 353 | if (disable) { 354 | uv_timer_stop(&info->ev); 355 | } 356 | else { 357 | uint64_t delta = calculateNextTimeout(info, now); 358 | uv_timer_t* tmp = &info->ev; 359 | uv_timer_start(tmp, &EventDispatcherLibUvPrivate::timer_callback, delta, 0); 360 | } 361 | 362 | ++it; 363 | } 364 | 365 | return true; 366 | } 367 | 368 | void EventDispatcherLibUvPrivate::killTimers(void) 369 | { 370 | if (!this->m_timers.isEmpty()) { 371 | TimerHash::Iterator it = this->m_timers.begin(); 372 | while (it != this->m_timers.end()) { 373 | TimerInfo* info = it.value(); 374 | uv_timer_stop(&info->ev); 375 | delete info; 376 | ++it; 377 | } 378 | 379 | this->m_timers.clear(); 380 | } 381 | } 382 | --------------------------------------------------------------------------------