diff options
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_win.cpp')
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_win.cpp | 1158 |
1 files changed, 1158 insertions, 0 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp new file mode 100644 index 0000000000..6badb6a2f8 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -0,0 +1,1158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_win_p.h" + +#include "qcoreapplication.h" +#include "qhash.h" +#include <private/qsystemlibrary_p.h> +#include "qpair.h" +#include "qset.h" +#include "qsocketnotifier.h" +#include "qvarlengtharray.h" +#include "qwineventnotifier_p.h" + +#include "qabstracteventdispatcher_p.h" +#include "qcoreapplication_p.h" +#include <private/qthread_p.h> +#include <private/qmutexpool_p.h> + +QT_BEGIN_NAMESPACE + +HINSTANCE qWinAppInst(); +extern uint qGlobalPostedEventsCount(); + +#ifndef TIME_KILL_SYNCHRONOUS +# define TIME_KILL_SYNCHRONOUS 0x0100 +#endif + +#ifndef QS_RAWINPUT +# ifdef Q_OS_WINCE +# define QS_RAWINPUT 0x0000 +# else +# define QS_RAWINPUT 0x0400 +# endif +#endif + +#ifndef WM_TOUCH +# define WM_TOUCH 0x0240 +#endif +#ifndef QT_NO_GESTURES +#ifndef WM_GESTURE +# define WM_GESTURE 0x0119 +#endif +#ifndef WM_GESTURENOTIFY +# define WM_GESTURENOTIFY 0x011A +#endif +#endif // QT_NO_GESTURES + +enum { + WM_QT_SOCKETNOTIFIER = WM_USER, + WM_QT_SENDPOSTEDEVENTS = WM_USER + 1, + SendPostedEventsWindowsTimerId = ~1u +}; + +#if defined(Q_OS_WINCE) +QT_BEGIN_INCLUDE_NAMESPACE +#include <winsock.h> +// Asynchronous Winsocks ------------------------------------------ +#ifndef QT_NO_THREAD +#include <qthread.h> +#include <qmap.h> +#include <qmutex.h> +QT_END_INCLUDE_NAMESPACE + +//#define QCE_ASYNC_DEBUG + +namespace { + class SocketAsyncHandler; + + class SocketAsyncHandler : public QThread + { + public: + SocketAsyncHandler(); + ~SocketAsyncHandler(); + void run(); + void select(SOCKET sock, HWND handle, unsigned int msg, long ev); + void removeSelect(SOCKET sock); + void safeRemove(SOCKET sock); + private: + struct SockInfo { + HWND handle; + unsigned int msg; + long ev; + }; + QMap<SOCKET, SockInfo> sockets; + QMutex mutex; + QWaitCondition cond; + bool supposedToDie; + }; + + SocketAsyncHandler::SocketAsyncHandler() + : supposedToDie(false) + { + } + + SocketAsyncHandler::~SocketAsyncHandler() + { + mutex.lock(); + supposedToDie = true; + mutex.unlock(); + cond.wakeOne(); + wait(); + while (sockets.size() > 0) + removeSelect(sockets.begin().key()); + } + + void SocketAsyncHandler::removeSelect(SOCKET sock) + { + if (!sockets.contains(sock)) + return; + sockets.remove(sock); + return; + } + + void SocketAsyncHandler::safeRemove(SOCKET sock) + { + QMutexLocker locker(&mutex); + removeSelect(sock); + } + + void SocketAsyncHandler::select(SOCKET sock, HWND handle, unsigned int msg, long ev) + { + QMutexLocker locker(&mutex); + + if (sockets.contains(sock)) + sockets.remove(sock); + + SockInfo info; + info.handle = handle; + info.msg = msg; + info.ev = ev; + sockets.insert(sock, info); + cond.wakeOne(); + } + + void SocketAsyncHandler::run() + { + do { + mutex.lock(); + + while (!supposedToDie && sockets.isEmpty()) { + cond.wait(&mutex); + } + + if (supposedToDie) { + mutex.unlock(); + break; + } + + // Copy current items to reduce lock time + // and to be able to use SendMessage + QMap<SOCKET, SockInfo> currentSockets = sockets; + mutex.unlock(); + + fd_set readS, writeS, exS; + FD_ZERO(&readS); + FD_ZERO(&writeS); + FD_ZERO(&exS); + + int maxFd = 0; + + for (QMap<SOCKET, SockInfo>::iterator it = currentSockets.begin(); it != currentSockets.end(); ++it) { + const SockInfo &info = it.value(); + int socket = it.key(); + maxFd = qMax(maxFd, socket); + + if ((info.ev & FD_READ) || (info.ev & FD_CLOSE) || (info.ev & FD_ACCEPT)) + FD_SET(socket, &readS); + if ((info.ev & FD_WRITE)|| (info.ev & FD_CONNECT)) + FD_SET(socket, &writeS); + if (info.ev & FD_OOB) + FD_SET(socket, &exS); + } + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; + int result = ::select(maxFd + 1, &readS, &writeS, &exS, &timeout); + if (result > 0) { + HWND handle; + unsigned int tmpMsg; + SOCKET sock; + HRESULT ret; + for (QMap<SOCKET, SockInfo>::const_iterator it = currentSockets.constBegin(); + it != currentSockets.constEnd(); ++it) { + handle = (*it).handle; + tmpMsg = (*it).msg; + sock = it.key(); + if (FD_ISSET(sock, &readS)) + ret = SendMessage(handle, tmpMsg, sock, FD_READ); + + if (FD_ISSET(sock, &writeS)) + ret = SendMessage(handle, tmpMsg, sock, FD_WRITE); + + if (FD_ISSET(sock, &exS)) + ret = SendMessage(handle, tmpMsg, sock, FD_OOB); + } + } + +#ifdef QCE_ASYNC_DEBUG + else if (result == 0) { //timeout + qDebug(" WSAAsync select timeout"); + } else if (result < 0) { // SocketError + // This might happen because of two reasons + // 1. We already closed a socket in between the copy and the select + // and thus select() returns an error + // 2. Something is really wrong, then + // ### Loop on all descriptors, try to select and remove the + // ### broken one. + qWarning("WSAAsync select error %d", WSAGetLastError()); + } +#endif + } while(true); + } +} // namespace + +Q_GLOBAL_STATIC(SocketAsyncHandler, qt_async_handler) + +int WSAAsyncSelect(SOCKET sock, HWND handle, unsigned int msg, long ev) +{ + if (sock == 0 || handle == 0 || handle == INVALID_HANDLE_VALUE) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + if (msg == 0 && ev == 0) + qt_async_handler()->safeRemove(sock); + else + qt_async_handler()->select(sock, handle, msg, ev); + + qt_async_handler()->start(QThread::LowPriority); + WSASetLastError(0); + return 0; +} +#else // QT_NO_THREAD +int WSAAsyncSelect(SOCKET, HWND, unsigned int, long) +{ + return SOCKET_ERROR; +} +#endif +#endif // Q_OS_WINCE + +class QEventDispatcherWin32Private; + +struct QSockNot { + QSocketNotifier *obj; + int fd; +}; +typedef QHash<int, QSockNot *> QSNDict; + +struct WinTimerInfo { // internal timer info + QObject *dispatcher; + int timerId; + int interval; + QObject *obj; // - object to receive events + bool inTimerEvent; + int fastTimerId; +}; + +class QZeroTimerEvent : public QTimerEvent +{ +public: + inline QZeroTimerEvent(int timerId) + : QTimerEvent(timerId) + { t = QEvent::ZeroTimerEvent; } +}; + +typedef QList<WinTimerInfo*> WinTimerVec; // vector of TimerInfo structs +typedef QHash<int, WinTimerInfo*> WinTimerDict; // fast dict of timers + +#if !defined(DWORD_PTR) && !defined(Q_WS_WIN64) +#define DWORD_PTR DWORD +#endif + +typedef MMRESULT(WINAPI *ptimeSetEvent)(UINT, UINT, LPTIMECALLBACK, DWORD_PTR, UINT); +typedef MMRESULT(WINAPI *ptimeKillEvent)(UINT); + +static ptimeSetEvent qtimeSetEvent = 0; +static ptimeKillEvent qtimeKillEvent = 0; + +LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); + +static void resolveTimerAPI() +{ + static bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + if (triedResolve) + return; +#endif + triedResolve = true; +#if !defined(Q_OS_WINCE) + qtimeSetEvent = (ptimeSetEvent)QSystemLibrary::resolve(QLatin1String("winmm"), "timeSetEvent"); + qtimeKillEvent = (ptimeKillEvent)QSystemLibrary::resolve(QLatin1String("winmm"), "timeKillEvent"); +#else + qtimeSetEvent = (ptimeSetEvent)QSystemLibrary::resolve(QLatin1String("Mmtimer"), "timeSetEvent"); + qtimeKillEvent = (ptimeKillEvent)QSystemLibrary::resolve(QLatin1String("Mmtimer"), "timeKillEvent"); +#endif + } +} + + +class QEventDispatcherWin32Private : public QAbstractEventDispatcherPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherWin32) +public: + QEventDispatcherWin32Private(); + ~QEventDispatcherWin32Private(); + + DWORD threadId; + + bool interrupt; + + // internal window handle used for socketnotifiers/timers/etc + HWND internalHwnd; + HHOOK getMessageHook; + + // for controlling when to send posted events + QAtomicInt serialNumber; + int lastSerialNumber, sendPostedEventsWindowsTimerId; + QAtomicInt wakeUps; + + // timers + WinTimerVec timerVec; + WinTimerDict timerDict; + void registerTimer(WinTimerInfo *t); + void unregisterTimer(WinTimerInfo *t, bool closingDown = false); + void sendTimerEvent(int timerId); + + // socket notifiers + QSNDict sn_read; + QSNDict sn_write; + QSNDict sn_except; + void doWsaAsyncSelect(int socket); + + QList<QWinEventNotifier *> winEventNotifierList; + void activateEventNotifier(QWinEventNotifier * wen); + + QList<MSG> queuedUserInputEvents; + QList<MSG> queuedSocketEvents; +}; + +QEventDispatcherWin32Private::QEventDispatcherWin32Private() + : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0), + serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0), wakeUps(0) +{ + resolveTimerAPI(); +} + +QEventDispatcherWin32Private::~QEventDispatcherWin32Private() +{ + if (internalHwnd) + DestroyWindow(internalHwnd); + QString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc)); + UnregisterClass((wchar_t*)className.utf16(), qWinAppInst()); +} + +void QEventDispatcherWin32Private::activateEventNotifier(QWinEventNotifier * wen) +{ + QEvent event(QEvent::WinEventAct); + QCoreApplication::sendEvent(wen, &event); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winPeekMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, + UINT wMsgFilterMax, UINT wRemoveMsg) +{ + return PeekMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return PostMessage(hWnd, msg, wParam, lParam); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winGetMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, + UINT wMsgFilterMax) +{ + return GetMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax); +} + +// This function is called by a workerthread +void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/) +{ + if (!timerId) // sanity check + return; + WinTimerInfo *t = (WinTimerInfo*)user; + Q_ASSERT(t); + QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId)); +} + +LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) +{ + if (message == WM_NCCREATE) + return true; + + MSG msg; + msg.hwnd = hwnd; + msg.message = message; + msg.wParam = wp; + msg.lParam = lp; + QCoreApplication *app = QCoreApplication::instance(); + long result; + if (!app) { + if (message == WM_TIMER) + KillTimer(hwnd, wp); + return 0; + } else if (app->filterEvent(&msg, &result)) { + return result; + } + +#ifdef GWLP_USERDATA + QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#else + QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA); +#endif + QEventDispatcherWin32Private *d = 0; + if (q != 0) + d = q->d_func(); + + if (message == WM_QT_SOCKETNOTIFIER) { + // socket notifier message + int type = -1; + switch (WSAGETSELECTEVENT(lp)) { + case FD_READ: + case FD_CLOSE: + case FD_ACCEPT: + type = 0; + break; + case FD_WRITE: + case FD_CONNECT: + type = 1; + break; + case FD_OOB: + type = 2; + break; + } + if (type >= 0) { + Q_ASSERT(d != 0); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + + QSockNot *sn = dict ? dict->value(wp) : 0; + if (sn) { + QEvent event(QEvent::SockAct); + QCoreApplication::sendEvent(sn->obj, &event); + } + } + return 0; + } else if (message == WM_QT_SENDPOSTEDEVENTS + // we also use a Windows timer to send posted events when the message queue is full + || (message == WM_TIMER + && d->sendPostedEventsWindowsTimerId != 0 + && wp == (uint)d->sendPostedEventsWindowsTimerId)) { + int localSerialNumber = d->serialNumber; + if (localSerialNumber != d->lastSerialNumber) { + d->lastSerialNumber = localSerialNumber; + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } + return 0; + } else if (message == WM_TIMER) { + Q_ASSERT(d != 0); + d->sendTimerEvent(wp); + return 0; + } + + return DefWindowProc(hwnd, message, wp, lp); +} + +LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) +{ + if (wp == PM_REMOVE) { + QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance()); + Q_ASSERT(q != 0); + if (q) { + MSG *msg = (MSG *) lp; + QEventDispatcherWin32Private *d = q->d_func(); + int localSerialNumber = d->serialNumber; + if (HIWORD(GetQueueStatus(QS_TIMER | QS_INPUT | QS_RAWINPUT)) == 0) { + // no more input or timer events in the message queue, we can allow posted events to be sent normally now + if (d->sendPostedEventsWindowsTimerId != 0) { + // stop the timer to send posted events, since we now allow the WM_QT_SENDPOSTEDEVENTS message + KillTimer(d->internalHwnd, d->sendPostedEventsWindowsTimerId); + d->sendPostedEventsWindowsTimerId = 0; + } + (void) d->wakeUps.fetchAndStoreRelease(0); + if (localSerialNumber != d->lastSerialNumber + // if this message IS the one that triggers sendPostedEvents(), no need to post it again + && (msg->hwnd != d->internalHwnd + || msg->message != WM_QT_SENDPOSTEDEVENTS)) { + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + } + } else if (d->sendPostedEventsWindowsTimerId == 0 + && localSerialNumber != d->lastSerialNumber) { + // start a special timer to continue delivering posted events while + // there are still input and timer messages in the message queue + d->sendPostedEventsWindowsTimerId = SetTimer(d->internalHwnd, + SendPostedEventsWindowsTimerId, + 0, // we specify zero, but Windows uses USER_TIMER_MINIMUM + NULL); + // we don't check the return value of SetTimer()... if creating the timer failed, there's little + // we can do. we just have to accept that posted events will be starved + } + } + } +#ifdef Q_OS_WINCE + return 0; +#else + return CallNextHookEx(0, code, wp, lp); +#endif +} + +static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher) +{ + // make sure that multiple Qt's can coexist in the same process + QString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc)); + + WNDCLASS wc; + wc.style = 0; + wc.lpfnWndProc = qt_internal_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = qWinAppInst(); + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = NULL; + wc.lpszClassName = reinterpret_cast<const wchar_t *> (className.utf16()); + + RegisterClass(&wc); + HWND wnd = CreateWindow(wc.lpszClassName, // classname + wc.lpszClassName, // window name + 0, // style + 0, 0, 0, 0, // geometry + 0, // parent + 0, // menu handle + qWinAppInst(), // application + 0); // windows creation data. + + if (!wnd) { + qWarning("QEventDispatcher: Failed to create QEventDispatcherWin32 internal window: %d\n", (int)GetLastError()); + } + +#ifdef GWLP_USERDATA + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher); +#else + SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher); +#endif + + return wnd; +} + +void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) +{ + Q_ASSERT(internalHwnd); + + Q_Q(QEventDispatcherWin32); + + int ok = 0; + if (t->interval > 20 || !t->interval || !qtimeSetEvent) { + ok = 1; + if (!t->interval) // optimization for single-shot-zero-timer + QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId)); + else + ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0); + } else { + ok = t->fastTimerId = qtimeSetEvent(t->interval, 1, qt_fast_timer_proc, (DWORD_PTR)t, + TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + if (ok == 0) { // fall back to normal timer if no more multimedia timers available + ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0); + } + } + + if (ok == 0) + qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer"); +} + +void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t, bool closingDown) +{ + // mark timer as unused + if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent && !closingDown) + QAbstractEventDispatcherPrivate::releaseTimerId(t->timerId); + + if (t->interval == 0) { + QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); + } else if (t->fastTimerId != 0) { + qtimeKillEvent(t->fastTimerId); + QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); + } else if (internalHwnd) { + KillTimer(internalHwnd, t->timerId); + } + delete t; +} + +void QEventDispatcherWin32Private::sendTimerEvent(int timerId) +{ + WinTimerInfo *t = timerDict.value(timerId); + if (t && !t->inTimerEvent) { + // send event, but don't allow it to recurse + t->inTimerEvent = true; + + QTimerEvent e(t->timerId); + QCoreApplication::sendEvent(t->obj, &e); + + // timer could have been removed + t = timerDict.value(timerId); + if (t) { + t->inTimerEvent = false; + } + } +} + +void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket) +{ + Q_ASSERT(internalHwnd); + int sn_event = 0; + if (sn_read.contains(socket)) + sn_event |= FD_READ | FD_CLOSE | FD_ACCEPT; + if (sn_write.contains(socket)) + sn_event |= FD_WRITE | FD_CONNECT; + if (sn_except.contains(socket)) + sn_event |= FD_OOB; + // BoundsChecker may emit a warning for WSAAsyncSelect when sn_event == 0 + // This is a BoundsChecker bug and not a Qt bug + WSAAsyncSelect(socket, internalHwnd, sn_event ? WM_QT_SOCKETNOTIFIER : 0, sn_event); +} + +void QEventDispatcherWin32::createInternalHwnd() +{ + Q_D(QEventDispatcherWin32); + + Q_ASSERT(!d->internalHwnd); + if (d->internalHwnd) + return; + d->internalHwnd = qt_create_internal_window(this); + +#ifndef Q_OS_WINCE + // setup GetMessage hook needed to drive our posted events + d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); + if (!d->getMessageHook) { + qFatal("Qt: INTERNALL ERROR: failed to install GetMessage hook"); + } +#endif + + // register all socket notifiers + QList<int> sockets = (d->sn_read.keys().toSet() + + d->sn_write.keys().toSet() + + d->sn_except.keys().toSet()).toList(); + for (int i = 0; i < sockets.count(); ++i) + d->doWsaAsyncSelect(sockets.at(i)); + + // start all normal timers + for (int i = 0; i < d->timerVec.count(); ++i) + d->registerTimer(d->timerVec.at(i)); + + // trigger a call to sendPostedEvents() + wakeUp(); +} + +QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent) + : QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent) +{ +} + +QEventDispatcherWin32::~QEventDispatcherWin32() +{ +} + +bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherWin32); + + if (!d->internalHwnd) + createInternalHwnd(); + + d->interrupt = false; + emit awake(); + + bool canWait; + bool retVal = false; + bool seenWM_QT_SENDPOSTEDEVENTS = false; + bool needWM_QT_SENDPOSTEDEVENTS = false; + do { + DWORD waitRet = 0; + HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; + QVarLengthArray<MSG> processedTimers; + while (!d->interrupt) { + DWORD nCount = d->winEventNotifierList.count(); + Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); + + MSG msg; + bool haveMessage; + + if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { + // process queued user input events + haveMessage = true; + msg = d->queuedUserInputEvents.takeFirst(); + } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { + // process queued socket events + haveMessage = true; + msg = d->queuedSocketEvents.takeFirst(); + } else { + haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); + if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) + && ((msg.message >= WM_KEYFIRST + && msg.message <= WM_KEYLAST) + || (msg.message >= WM_MOUSEFIRST + && msg.message <= WM_MOUSELAST) + || msg.message == WM_MOUSEWHEEL + || msg.message == WM_MOUSEHWHEEL + || msg.message == WM_TOUCH +#ifndef QT_NO_GESTURES + || msg.message == WM_GESTURE + || msg.message == WM_GESTURENOTIFY +#endif + || msg.message == WM_CLOSE)) { + // queue user input events for later processing + haveMessage = false; + d->queuedUserInputEvents.append(msg); + } + if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) + && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { + // queue socket events for later processing + haveMessage = false; + d->queuedSocketEvents.append(msg); + } + } + if (!haveMessage) { + // no message - check for signalled objects + for (int i=0; i<(int)nCount; i++) + pHandles[i] = d->winEventNotifierList.at(i)->handle(); + waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); + if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { + // a new message has arrived, process it + continue; + } + } + if (haveMessage) { +#ifdef Q_OS_WINCE + // WinCE doesn't support hooks at all, so we have to call this by hand :( + (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg); +#endif + + if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { + if (seenWM_QT_SENDPOSTEDEVENTS) { + // when calling processEvents() "manually", we only want to send posted + // events once + needWM_QT_SENDPOSTEDEVENTS = true; + continue; + } + seenWM_QT_SENDPOSTEDEVENTS = true; + } else if (msg.message == WM_TIMER) { + // avoid live-lock by keeping track of the timers we've already sent + bool found = false; + for (int i = 0; !found && i < processedTimers.count(); ++i) { + const MSG processed = processedTimers.constData()[i]; + found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); + } + if (found) + continue; + processedTimers.append(msg); + } else if (msg.message == WM_QUIT) { + if (QCoreApplication::instance()) + QCoreApplication::instance()->quit(); + return false; + } + + if (!filterEvent(&msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { + d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + } else { + // nothing todo so break + break; + } + retVal = true; + } + + // still nothing - wait for message or signalled objects + canWait = (!retVal + && !d->interrupt + && (flags & QEventLoop::WaitForMoreEvents)); + if (canWait) { + DWORD nCount = d->winEventNotifierList.count(); + Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); + for (int i=0; i<(int)nCount; i++) + pHandles[i] = d->winEventNotifierList.at(i)->handle(); + + emit aboutToBlock(); + waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + emit awake(); + if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { + d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + retVal = true; + } + } + } while (canWait); + + if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) { + // when called "manually", always send posted events + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } + + if (needWM_QT_SENDPOSTEDEVENTS) + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + + return retVal; +} + +bool QEventDispatcherWin32::hasPendingEvents() +{ + MSG msg; + return qGlobalPostedEventsCount() || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); +} + +void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherWin32); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + + if (QCoreApplication::closingDown()) // ### d->exitloop? + return; // after sn_cleanup, don't reinitialize. + + if (dict->contains(sockfd)) { + const char *t[] = { "Read", "Write", "Exception" }; + /* Variable "socket" below is a function pointer. */ + qWarning("QSocketNotifier: Multiple socket notifiers for " + "same socket %d and type %s", sockfd, t[type]); + } + + QSockNot *sn = new QSockNot; + sn->obj = notifier; + sn->fd = sockfd; + dict->insert(sn->fd, sn); + + if (d->internalHwnd) + d->doWsaAsyncSelect(sockfd); +} + +void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherWin32); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + QSockNot *sn = dict->value(sockfd); + if (!sn) + return; + + dict->remove(sockfd); + delete sn; + + if (d->internalHwnd) + d->doWsaAsyncSelect(sockfd); +} + +void QEventDispatcherWin32::registerTimer(int timerId, int interval, QObject *object) +{ + if (timerId < 1 || interval < 0 || !object) { + qWarning("QEventDispatcherWin32::registerTimer: invalid arguments"); + return; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } + + Q_D(QEventDispatcherWin32); + + register WinTimerInfo *t = new WinTimerInfo; + t->dispatcher = this; + t->timerId = timerId; + t->interval = interval; + t->obj = object; + t->inTimerEvent = false; + t->fastTimerId = 0; + + if (d->internalHwnd) + d->registerTimer(t); + + d->timerVec.append(t); // store in timer vector + d->timerDict.insert(t->timerId, t); // store timers in dict +} + +bool QEventDispatcherWin32::unregisterTimer(int timerId) +{ + if (timerId < 1) { + qWarning("QEventDispatcherWin32::unregisterTimer: invalid argument"); + return false; + } + QThread *currentThread = QThread::currentThread(); + if (thread() != currentThread) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + if (d->timerVec.isEmpty() || timerId <= 0) + return false; + + WinTimerInfo *t = d->timerDict.value(timerId); + if (!t) + return false; + + d->timerDict.remove(t->timerId); + d->timerVec.removeAll(t); + d->unregisterTimer(t); + return true; +} + +bool QEventDispatcherWin32::unregisterTimers(QObject *object) +{ + if (!object) { + qWarning("QEventDispatcherWin32::unregisterTimers: invalid argument"); + return false; + } + QThread *currentThread = QThread::currentThread(); + if (object->thread() != thread() || thread() != currentThread) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + if (d->timerVec.isEmpty()) + return false; + register WinTimerInfo *t; + for (int i=0; i<d->timerVec.size(); i++) { + t = d->timerVec.at(i); + if (t && t->obj == object) { // object found + d->timerDict.remove(t->timerId); + d->timerVec.removeAt(i); + d->unregisterTimer(t); + --i; + } + } + return true; +} + +QList<QEventDispatcherWin32::TimerInfo> +QEventDispatcherWin32::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherWin32:registeredTimers: invalid argument"); + return QList<TimerInfo>(); + } + + Q_D(const QEventDispatcherWin32); + QList<TimerInfo> list; + for (int i = 0; i < d->timerVec.size(); ++i) { + const WinTimerInfo *t = d->timerVec.at(i); + if (t && t->obj == object) + list << TimerInfo(t->timerId, t->interval); + } + return list; +} + +bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier) +{ + if (!notifier) { + qWarning("QWinEventNotifier: Internal error"); + return false; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QWinEventNotifier: event notifiers cannot be enabled from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + + if (d->winEventNotifierList.contains(notifier)) + return true; + + if (d->winEventNotifierList.count() >= MAXIMUM_WAIT_OBJECTS - 2) { + qWarning("QWinEventNotifier: Cannot have more than %d enabled at one time", MAXIMUM_WAIT_OBJECTS - 2); + return false; + } + d->winEventNotifierList.append(notifier); + return true; +} + +void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier) +{ + if (!notifier) { + qWarning("QWinEventNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QWinEventNotifier: event notifiers cannot be disabled from another thread"); + return; + } + + Q_D(QEventDispatcherWin32); + + int i = d->winEventNotifierList.indexOf(notifier); + if (i != -1) + d->winEventNotifierList.takeAt(i); +} + +void QEventDispatcherWin32::activateEventNotifiers() +{ + Q_D(QEventDispatcherWin32); + //### this could break if events are removed/added in the activation + for (int i=0; i<d->winEventNotifierList.count(); i++) { +#if !defined(Q_OS_WINCE) + if (WaitForSingleObjectEx(d->winEventNotifierList.at(i)->handle(), 0, TRUE) == WAIT_OBJECT_0) + d->activateEventNotifier(d->winEventNotifierList.at(i)); +#else + if (WaitForSingleObject(d->winEventNotifierList.at(i)->handle(), 0) == WAIT_OBJECT_0) + d->activateEventNotifier(d->winEventNotifierList.at(i)); +#endif + } +} + +void QEventDispatcherWin32::wakeUp() +{ + Q_D(QEventDispatcherWin32); + d->serialNumber.ref(); + if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) { + // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + } +} + +void QEventDispatcherWin32::interrupt() +{ + Q_D(QEventDispatcherWin32); + d->interrupt = true; + wakeUp(); +} + +void QEventDispatcherWin32::flush() +{ } + +void QEventDispatcherWin32::startingUp() +{ } + +void QEventDispatcherWin32::closingDown() +{ + Q_D(QEventDispatcherWin32); + + // clean up any socketnotifiers + while (!d->sn_read.isEmpty()) + unregisterSocketNotifier((*(d->sn_read.begin()))->obj); + while (!d->sn_write.isEmpty()) + unregisterSocketNotifier((*(d->sn_write.begin()))->obj); + while (!d->sn_except.isEmpty()) + unregisterSocketNotifier((*(d->sn_except.begin()))->obj); + + // clean up any timers + for (int i = 0; i < d->timerVec.count(); ++i) + d->unregisterTimer(d->timerVec.at(i), true); + d->timerVec.clear(); + d->timerDict.clear(); + +#ifndef Q_OS_WINCE + if (d->getMessageHook) + UnhookWindowsHookEx(d->getMessageHook); + d->getMessageHook = 0; +#endif +} + +bool QEventDispatcherWin32::event(QEvent *e) +{ + Q_D(QEventDispatcherWin32); + if (e->type() == QEvent::ZeroTimerEvent) { + QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e); + WinTimerInfo *t = d->timerDict.value(zte->timerId()); + if (t) { + t->inTimerEvent = true; + + QTimerEvent te(zte->timerId()); + QCoreApplication::sendEvent(t->obj, &te); + + t = d->timerDict.value(zte->timerId()); + if (t) { + if (t->interval == 0 && t->inTimerEvent) { + // post the next zero timer event as long as the timer was not restarted + QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId())); + } + + t->inTimerEvent = false; + } + } + return true; + } else if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast<QTimerEvent*>(e); + d->sendTimerEvent(te->timerId()); + } + return QAbstractEventDispatcher::event(e); +} + +QT_END_NAMESPACE |