| /**************************************************************************** |
| ** |
| ** 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** 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. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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 |
| # 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 == 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 |