/**************************************************************************** | |
** | |
** 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 |