/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** 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 either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** 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.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qeventdispatcher_win_p.h" #include "qcoreapplication.h" #include "qhash.h" #include "qlibrary.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 #include QT_BEGIN_NAMESPACE HINSTANCE qWinAppInst(); extern uint qGlobalPostedEventsCount(); #ifndef TIME_KILL_SYNCHRONOUS # define TIME_KILL_SYNCHRONOUS 0x0100 #endif #if defined(Q_OS_WINCE) QT_BEGIN_INCLUDE_NAMESPACE #include // Asynchronous Winsocks ------------------------------------------ #ifndef QT_NO_THREAD #include #include #include 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 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 currentSockets = sockets; mutex.unlock(); fd_set readS, writeS, exS; FD_ZERO(&readS); FD_ZERO(&writeS); FD_ZERO(&exS); int maxFd = 0; for (QMap::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::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 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 WinTimerVec; // vector of TimerInfo structs typedef QHash 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 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)QLibrary::resolve(QLatin1String("winmm"), "timeSetEvent"); qtimeKillEvent = (ptimeKillEvent)QLibrary::resolve(QLatin1String("winmm"), "timeKillEvent"); #else qtimeSetEvent = (ptimeSetEvent)QLibrary::resolve(QLatin1String("Mmtimer"), "timeSetEvent"); qtimeKillEvent = (ptimeKillEvent)QLibrary::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; // timers WinTimerVec timerVec; WinTimerDict timerDict; void registerTimer(WinTimerInfo *t); void unregisterTimer(WinTimerInfo *t); void sendTimerEvent(int timerId); // socket notifiers QSNDict sn_read; QSNDict sn_write; QSNDict sn_except; void doWsaAsyncSelect(int socket); // event notifier QWinEventNotifier wakeUpNotifier; QList winEventNotifierList; void activateEventNotifier(QWinEventNotifier * wen); QList queuedUserInputEvents; QList queuedSocketEvents; }; QEventDispatcherWin32Private::QEventDispatcherWin32Private() : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0) { resolveTimerAPI(); wakeUpNotifier.setHandle(CreateEvent(0, FALSE, FALSE, 0)); if (!wakeUpNotifier.handle()) qWarning("QEventDispatcher: Creating QEventDispatcherWin32Private wakeup event failed"); } QEventDispatcherWin32Private::~QEventDispatcherWin32Private() { wakeUpNotifier.setEnabled(false); CloseHandle(wakeUpNotifier.handle()); 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 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 CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_NCCREATE) { return true; } else if (message == WM_USER) { // socket notifier message MSG msg; msg.hwnd = hwnd; msg.message = message; msg.wParam = wp; msg.lParam = lp; QCoreApplication *app = QCoreApplication::instance(); long result; if (app && app->filterEvent(&msg, &result)) return result; 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) { #ifdef GWLP_USERDATA QEventDispatcherWin32 *eventDispatcher = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA); #else QEventDispatcherWin32 *eventDispatcher = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA); #endif if (eventDispatcher) { QEventDispatcherWin32Private *d = eventDispatcher->d_func(); 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_TIMER) { MSG msg; msg.hwnd = hwnd; msg.message = message; msg.wParam = wp; msg.lParam = lp; QCoreApplication *app = QCoreApplication::instance(); Q_ASSERT_X(app, "qt_interal_proc", "Timer fired, but no QCoreApplication"); if (!app) { KillTimer(hwnd, wp); return 0; } long result; if (app->filterEvent(&msg, &result)) return result; QEventDispatcherWin32 *eventDispatcher = qobject_cast(QAbstractEventDispatcher::instance()); Q_ASSERT(eventDispatcher != 0); QEventDispatcherWin32Private *d = eventDispatcher->d_func(); d->sendTimerEvent(wp); return 0; } return DefWindowProc(hwnd, message, wp, lp); } 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 (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. #ifdef GWLP_USERDATA SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher); #else SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher); #endif if (!wnd) { qWarning("QEventDispatcher: Failed to create QEventDispatcherWin32 internal window: %d\n", (int)GetLastError()); } return wnd; } void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) { Q_ASSERT(internalHwnd); Q_Q(QEventDispatcherWin32); int ok = 0; if (t->interval > 10 || !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 avaiable 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) { // mark timer as unused if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent) 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_USER : 0, sn_event); } void QEventDispatcherWin32::createInternalHwnd() { Q_D(QEventDispatcherWin32); Q_ASSERT(!d->internalHwnd); if (d->internalHwnd) return; d->internalHwnd = qt_create_internal_window(this); // register all socket notifiers QList 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)); } 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; do { QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); DWORD waitRet = 0; HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; QVarLengthArray 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)) { // queue user input events for later processing haveMessage = false; d->queuedUserInputEvents.append(msg); } if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) && (msg.message == WM_USER && 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) { 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 QThreadData *data = d->threadData; canWait = (!retVal && data->canWait && !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); 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); 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; itimerVec.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::registeredTimers(QObject *object) const { if (!object) { qWarning("QEventDispatcherWin32:registeredTimers: invalid argument"); return QList(); } Q_D(const QEventDispatcherWin32); QList 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; iwinEventNotifierList.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); SetEvent(d->wakeUpNotifier.handle()); } void QEventDispatcherWin32::interrupt() { Q_D(QEventDispatcherWin32); d->interrupt = true; wakeUp(); } void QEventDispatcherWin32::flush() { } void QEventDispatcherWin32::startingUp() { Q_D(QEventDispatcherWin32); if (d->wakeUpNotifier.handle()) d->wakeUpNotifier.setEnabled(true); } 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 while (!d->timerDict.isEmpty()) unregisterTimer((*(d->timerDict.begin()))->timerId); } bool QEventDispatcherWin32::event(QEvent *e) { Q_D(QEventDispatcherWin32); if (e->type() == QEvent::ZeroTimerEvent) { QZeroTimerEvent *zte = static_cast(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(e); d->sendTimerEvent(te->timerId()); } return QAbstractEventDispatcher::event(e); } QT_END_NAMESPACE