source: trunk/src/corelib/kernel/qeventdispatcher_pm.cpp @ 83

Last change on this file since 83 was 83, checked in by Dmitry A. Kuminov, 11 years ago

corelib: Implemented socket notifiers (#24).

File size: 37.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtCore module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file.  Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file.  Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you are unsure which license is appropriate for your use, please
39** contact the sales department at qt-sales@nokia.com.
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qeventdispatcher_pm_p.h"
45
46#include "qcoreapplication.h"
47#include "qhash.h"
48#include "qsocketnotifier.h"
49#include "qmutex.h"
50#include "qwaitcondition.h"
51
52#include "qabstracteventdispatcher_p.h"
53#include "qcoreapplication_p.h"
54
55#include <private/qthread_p.h>
56#include <private/qmutexpool_p.h>
57
58#include <Qt/qwindowdefs_pm.h> // for QPMObjectWindow declaration
59
60#include <sys/socket.h>
61
62QT_BEGIN_NAMESPACE
63
64extern uint qGlobalPostedEventsCount();
65
66class QEventDispatcherPMPrivate;
67
68// @todo later: timers
69#if 0
70
71struct WinTimerInfo {                           // internal timer info
72    QObject *dispatcher;
73    int timerId;
74    int interval;
75    QObject *obj;                               // - object to receive events
76    bool inTimerEvent;
77    int fastTimerId;
78};
79
80class QZeroTimerEvent : public QTimerEvent
81{
82public:
83    inline QZeroTimerEvent(int timerId)
84        : QTimerEvent(timerId)
85    { t = QEvent::ZeroTimerEvent; }
86};
87
88typedef QList<WinTimerInfo*>  WinTimerVec;      // vector of TimerInfo structs
89typedef QHash<int, WinTimerInfo*> WinTimerDict; // fast dict of timers
90
91#if !defined(DWORD_PTR) && !defined(Q_WS_WIN64)
92#define DWORD_PTR DWORD
93#endif
94
95typedef MMRESULT(WINAPI *ptimeSetEvent)(UINT, UINT, LPTIMECALLBACK, DWORD_PTR, UINT);
96typedef MMRESULT(WINAPI *ptimeKillEvent)(UINT);
97
98static ptimeSetEvent qtimeSetEvent = 0;
99static ptimeKillEvent qtimeKillEvent = 0;
100
101LRESULT CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
102
103static void resolveTimerAPI()
104{
105    static bool triedResolve = false;
106    if (!triedResolve) {
107#ifndef QT_NO_THREAD
108        QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
109        if (triedResolve)
110            return;
111#endif
112        triedResolve = true;
113#if !defined(Q_OS_WINCE)
114        qtimeSetEvent = (ptimeSetEvent)QLibrary::resolve(QLatin1String("winmm"), "timeSetEvent");
115        qtimeKillEvent = (ptimeKillEvent)QLibrary::resolve(QLatin1String("winmm"), "timeKillEvent");
116#else
117        qtimeSetEvent = (ptimeSetEvent)QLibrary::resolve(QLatin1String("Mmtimer"), "timeSetEvent");
118        qtimeKillEvent = (ptimeKillEvent)QLibrary::resolve(QLatin1String("Mmtimer"), "timeKillEvent");
119#endif
120    }
121}
122
123#endif
124
125/*****************************************************************************
126  Auxiliary object window class for dedicated message processing.
127  Declared in qwindowdefs_pm.h
128 *****************************************************************************/
129
130/*!
131    \class QPMObjectWindow qwindowdefs.h
132
133    The QPMObjectWindow class is an auxiliary class for dedicated message
134    processing. Its functionality is based on PM object windows. Once an
135    instance of this class is created, PM window messages can be sent or posted
136    to it using send() or post() methods. Subclasses should override the
137    message() method to process sent or posted messages. The hwnd() method is
138    used whenever a PM window handle of this object window is necessary to be
139    passed as a HWND argument to other calls and/or messages.
140
141    Instances of this class may be created only on the main GUI thread or on a
142    thread that has created a PM message queue and runs the PM message loop
143    \b itself. If you create an instance on a thread other than main, make sure
144    you destroy it before destroying the thread's message queue.
145
146    \note WM_CREATE and WM_DESTROY messages are processed internally and not
147    delivered do the message() method. Instead, you can use the constructor and
148    the destructor of the subclass, respectively.
149
150    \note This class is OS/2 specific and not available in Qt for other
151    platforms!
152*/
153
154/*!
155    Constructs a new object window for the current thread.
156    If \a deferred is \c false, this method calls create() to create a PM object
157    window. Otherwise, you must call create() yourself before this object window
158    is able to process messages.
159*/
160QPMObjectWindow::QPMObjectWindow(bool deferred /* = false */) :
161    w(NULLHANDLE)
162{
163    if (!deferred)
164        create();
165}
166
167/*!
168    Destroys this object window.
169    This method calls destroy() to free the PM object window.
170*/
171QPMObjectWindow::~QPMObjectWindow()
172{
173    destroy();
174}
175
176/*!
177    Creates a PM object window.
178    Returns \c true on success or \c false otherwise.
179    The method does nothing but returns \c false if the window has been already
180    created. The handle of the successfully created object window can be obtained
181    using the hwnd() method.
182
183    \note Must be called on the same thread that cosnstructed this instance.
184*/
185bool QPMObjectWindow::create()
186{
187    if (w != NULLHANDLE)
188        return false;
189
190    static const char *ClassName = "QPMObjectWindow";
191    static bool classRegistered = FALSE;
192
193    if (!classRegistered) {
194        WinRegisterClass(0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID));
195        classRegistered = true;
196    }
197
198    w = WinCreateWindow(HWND_OBJECT, ClassName,
199                        NULL, 0, 0, 0, 0, 0, NULL,
200                        HWND_BOTTOM, 0, this, NULL);
201    if (w == NULLHANDLE)
202        qWarning("QPMObjectWindow::create: WinCreateWindow failed with 0x%08lX",
203                 WinGetLastError(0));
204
205    return w != NULLHANDLE;
206}
207
208/*!
209    Destroys the PM object window.
210    Returns \c TRUE on success or \c FALSE otherwise.
211    The method does nothing but returns \c FALSE  if the window has been
212    already destroyed (or never created).
213
214    \note Must be called on the same thread that cosnstructed this instance.
215*/
216bool QPMObjectWindow::destroy()
217{
218    if (w == NULLHANDLE)
219        return false;
220
221    HWND h = w;
222    w = NULLHANDLE; // tell windowProc() we're unsafe
223    WinDestroyWindow(h);
224
225    return true;
226}
227
228//static
229MRESULT EXPENTRY QPMObjectWindow::windowProc(HWND hwnd, ULONG msg,
230                                             MPARAM mp1, MPARAM mp2)
231{
232    if (msg == WM_CREATE) {
233        QPMObjectWindow *that = static_cast<QPMObjectWindow *>(mp1);
234        if (!that)
235            return (MRESULT) TRUE;
236        WinSetWindowPtr(hwnd, QWL_USER, that);
237        return (MRESULT) FALSE;
238    }
239
240    QPMObjectWindow *that =
241        static_cast<QPMObjectWindow *>(WinQueryWindowPtr(hwnd, QWL_USER));
242    Q_ASSERT(that);
243
244    // Note: before WinCreateWindow() returns to the constructor or after the
245    // destructor has been called, w is 0. We use this to determine that the
246    // object is in the unsafe state (VTBL is not yet initialized or has been
247    // already uninitialized), so message() points to never-never land.
248    if (!that || !that->w)
249        return (MRESULT) FALSE;
250
251    return that->message(msg, mp1, mp2);
252}
253
254/*!
255    \fn QPMObjectWindow::send(ULONG msg, MPARAM mp1, MPARAM mp2) const
256
257    Synchronously sends a message \a msg with the given parameters \a mp1 and
258    \a mp2 to this window handle and returns a reply from the message() function.
259
260    \note Must be called on the same thread that cosnstructed this instance.
261*/
262
263/*!
264    \fn QPMObjectWindow::post(ULONG msg, MPARAM mp1, MPARAM mp2) const
265
266    Asynchronously posts a message \a msg with the given parameters \a mp1 and
267    \a mp2 to this window handle. Returns \c true on success and \c false
268    otherwise.
269
270    \note Can be called on any thread.
271*/
272
273
274// socket select notification (highest priority)
275#define WM_U_SEM_SELECT     WM_SEM1
276// zero timer notification (lowest priority)
277#define WM_U_SEM_ZEROTIMER  WM_SEM4
278
279/*****************************************************************************
280 socket select() thread
281 *****************************************************************************/
282
283#if !defined(QT_NO_THREAD)
284
285class QSockSelectThread : public QThread
286{
287public:
288    typedef QSocketNotifier::Type Type;
289
290    static void addSelect(QSocketNotifier *notifier, HWND hwnd);
291    static void removeSelect(QSocketNotifier *notifier);
292    static QSocketNotifier *getSocketNotifier(int key);
293
294    static void attachThread();
295    static void detachThread();
296
297private:
298    QSockSelectThread() : finish(false), refcnt(0), maxSockfd(-1) {};
299
300    void run();
301    void cancelSelectOrIdle();
302
303    bool finish;
304    int refcnt;
305    QWaitCondition cond;
306
307    typedef QHash<int, QSocketNotifier*> Sockets;
308    Sockets sockets;
309    int maxSockfd;
310
311    enum Op { Add, Remove };
312    struct PendingOp {
313        Op op;
314        int sockfd;
315        Type type;
316        HWND hwnd;
317    };
318    typedef QList<PendingOp> Pending;
319    Pending pending;
320
321    static int toKey(int sockfd, Type type) {
322        // as a hash key, we use first two bits of int for the type and the rest
323        // for the sockfd which should be enough in the real world
324        Q_ASSERT(((sockfd << 2) >> 2) == sockfd);
325        return (sockfd << 2) + type;
326    }
327    // opposite to toKey()
328    static int toSocket(int key) { return key >> 2; }
329    static Type toType(int key) { return Type(key & 0x3); }
330
331    static QSockSelectThread *instance;
332    static QMutex mutex;
333};
334
335// static
336QSockSelectThread *QSockSelectThread::instance = 0;
337QMutex QSockSelectThread::mutex;
338
339// static
340void QSockSelectThread::addSelect(QSocketNotifier *notifier, HWND hwnd)
341{
342    Q_ASSERT(hwnd != NULLHANDLE);
343    if (hwnd == NULLHANDLE)
344        return;
345
346    int sockfd = notifier->socket();
347    Type type = notifier->type();
348    int key = toKey(sockfd, type);
349
350    QMutexLocker locker(&mutex);
351    Q_ASSERT(instance);
352
353    instance->start();
354
355    if (instance->sockets.contains(key)) {
356        static const char *t[] = { "Read", "Write", "Exception" };
357        qWarning("QSocketNotifier: Multiple socket notifiers for "
358                 "same socket %d and type %s", sockfd, t[type]);
359
360    }
361    instance->sockets.insert(key, notifier);
362    PendingOp op = {Add, sockfd, type, hwnd};
363    instance->pending.append(op);
364    instance->cancelSelectOrIdle();
365}
366
367// static
368void QSockSelectThread::removeSelect(QSocketNotifier *notifier)
369{
370    QMutexLocker locker(&mutex);
371    Q_ASSERT(instance);
372
373    int sockfd = notifier->socket();
374    Type type = notifier->type();
375    int key = toKey(sockfd, notifier->type());
376
377    if (instance->sockets.contains(key)) {
378        instance->sockets.remove(key);
379        PendingOp op = {Remove, sockfd, type};
380        instance->pending.append(op);
381        instance->cancelSelectOrIdle();
382    }
383}
384
385/*!
386    Returns the socket notifier object corresponding to the given key from the
387    WM_U_SEM_SELECT message. The return value is only correct for the thread
388    that owns QSocketNotifier (creates/registers/unregisters it).
389
390    May return 0 if the socket notifier object is disabled/deleted after
391    WM_U_SEM_SELECT is issued for it but before this message gets processed by
392    the owning thread.
393*/
394// static
395QSocketNotifier *QSockSelectThread::getSocketNotifier(int key)
396{
397    QMutexLocker locker(&mutex);
398    Q_ASSERT(instance);
399
400    if (instance->sockets.contains(key) &&
401        instance->sockets[key]->thread() == QThread::currentThread())
402        return instance->sockets[key];
403
404    return 0;
405}
406
407/*!
408    Incrreases the usage count of the QSockSelectThread instance by one. If no
409    QSockSelectThread instance exists yet, creates it. Note that the thread is
410    started on demand by addSelect(), not by this method.
411
412    Must be called once per thread before any other call and must be completed
413    by the detachThread() call when socket select functionality is no more
414    necassary.
415*/
416// static
417void QSockSelectThread::attachThread()
418{
419    QMutexLocker locker(&mutex);
420
421    if (instance == 0) {
422        instance = new QSockSelectThread();
423        instance->start();
424    }
425
426    ++instance->refcnt;
427}
428
429/*!
430    Removes all socket notifiers owned by the current thread and decreases the
431    usage count of the QSockSelectThread instance by one. When the usage count
432    goes to zero, the socket select thread is stopped and the instance is
433    deleted.
434
435    May only be called once per thread and only if attachThread() was called on
436    this thread before.
437*/
438// static
439void QSockSelectThread::detachThread()
440{
441    QMutexLocker locker(&mutex);
442    Q_ASSERT(instance);
443
444    for (Sockets::iterator it = instance->sockets.begin();
445          it != instance->sockets.end();) {
446        QSocketNotifier *notifier = it.value();
447        if (notifier->thread() == QThread::currentThread()) {
448            PendingOp op = {Remove, notifier->socket(), notifier->type()};
449            instance->pending.append(op);
450            it = instance->sockets.erase(it);
451        } else {
452            ++it;
453        }
454    }
455
456    if (--instance->refcnt == 0) {
457        instance->finish = true;
458        instance->cancelSelectOrIdle();
459        instance->wait();
460        delete instance;
461        instance = 0;
462    } else {
463        instance->cancelSelectOrIdle();
464    }
465}
466void QSockSelectThread::run()
467{
468    // maintain a separate hash for HWNDs to avoid mutex locking every time
469    // select() returns an event that we want to post
470    typedef QHash<int, HWND> Hwnds;
471    Hwnds hwnds;
472
473    fd_set readS, writeS, exS;
474    FD_ZERO(&readS);
475    FD_ZERO(&writeS);
476    FD_ZERO(&exS);
477
478    do {
479        mutex.lock();
480
481        while (!finish && sockets.isEmpty()) {
482            cond.wait(&mutex);
483        }
484
485        if (finish) {
486            mutex.unlock();
487            break;
488        }
489
490        while (!pending.isEmpty()) {
491            PendingOp p = pending.takeFirst();
492            switch (p.op) {
493                case Add:
494                    switch (p.type) {
495                        case QSocketNotifier::Read:
496                            FD_SET(p.sockfd, &readS); break;
497                        case QSocketNotifier::Write:
498                            FD_SET(p.sockfd, &writeS); break;
499                        case QSocketNotifier::Exception:
500                            FD_SET(p.sockfd, &exS); break;
501                    }
502                    hwnds.insert(toKey(p.sockfd, p.type), p.hwnd);
503                    maxSockfd = qMax(maxSockfd, p.sockfd);
504                    break;
505                case Remove:
506                    switch (p.type) {
507                        case QSocketNotifier::Read:
508                            FD_CLR(p.sockfd, &readS); break;
509                        case QSocketNotifier::Write:
510                            FD_CLR(p.sockfd, &writeS); break;
511                        case QSocketNotifier::Exception:
512                            FD_CLR(p.sockfd, &exS); break;
513                    }
514                    hwnds.remove(toKey(p.sockfd, p.type));
515                    if (maxSockfd == p.sockfd) {
516                        // find the new hignest socket
517                        if (hwnds.isEmpty()) {
518                            maxSockfd = -1;
519                        } else {
520                            for (Hwnds::const_iterator it = hwnds.constBegin();
521                                  it != hwnds.constEnd(); ++it) {
522                                maxSockfd = qMax(toSocket(it.key()), maxSockfd);
523                            }
524                        }
525                    }
526                    break;
527            }
528        }
529
530        // do select
531        mutex.unlock();
532        int nsel = ::select(maxSockfd + 1, &readS, &writeS, &exS, NULL);
533        if (nsel > 0) {
534            for (Hwnds::const_iterator it = hwnds.constBegin();
535                  it != hwnds.constEnd(); ++it) {
536                int sockfd = toSocket(it.key());
537                bool isSet = false;
538                switch (toType(it.key())) {
539                    case QSocketNotifier::Read:
540                        isSet = FD_ISSET(sockfd, &readS); break;
541                    case QSocketNotifier::Write:
542                        isSet = FD_ISSET(sockfd, &writeS); break;
543                    case QSocketNotifier::Exception:
544                        isSet = FD_ISSET(sockfd, &exS); break;
545                }
546                if (isSet)
547                    WinPostMsg(it.value(), WM_U_SEM_SELECT, MPFROMLONG(it.key()), 0);
548            }
549        }
550    } while(true);
551}
552
553// Must be called from under QSockSelectThread::mutex
554void QSockSelectThread::cancelSelectOrIdle()
555{
556    if (maxSockfd >= 0) {
557        // terminate select() execution
558        ::so_cancel(maxSockfd);
559    } else {
560        // terminate the idle state
561        cond.wakeOne();
562    }
563}
564
565#else
566
567class QSockSelectThread
568{
569public:
570    static void addSelect(QSocketNotifier *notifier, HWND hwnd) {
571#ifndef QT_NO_DEBUG
572        qWarning("QSocketNotifier: socket notifiers require thread support but"
573                 "QT_NO_THREAD was defined");
574#endif
575    }
576    static void removeSelect(QSocketNotifier *notifier) {}
577    static QSocketNotifier *getSocketNotifier(int key); { return 0; }
578
579    static void attachThread() {}
580    static void detachThread() {}
581};
582
583#endif
584
585class QEventDispatcherPMPrivate : public QAbstractEventDispatcherPrivate
586{
587    Q_DECLARE_PUBLIC(QEventDispatcherPM)
588public:
589    QEventDispatcherPMPrivate();
590    ~QEventDispatcherPMPrivate();
591
592    void createMsgQueue();
593    void createAuxWnd();
594
595    // Auxiliary object window to process WM_U_SEM_SELECT and WM_TIMER messages.
596    // We need a dedicated window along with processing these messages directly in
597    // QEventLoop::processEvents() to make sure they are processed even if the
598    // current message loop is not run by Qt. This happens when a native modal
599    // dialog is shown or when a top-level Qt widget is being moved or resized
600    // using the mouse, or when a Qt-based DLL plugin is used by a non-Qt
601    // application.
602    class AuxWnd : public QPMObjectWindow
603    {
604    public:
605        AuxWnd() : QPMObjectWindow(true /* deferred */), dispatcher(0) {}
606        void setDispatcher(QEventDispatcherPMPrivate *d) { dispatcher = d; }
607        MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
608        QEventDispatcherPMPrivate *dispatcher;
609    } auxWnd;
610
611    HAB hab;
612    HMQ hmq;
613
614    bool interrupt;
615
616// @todo later
617//
618//  // timers
619//  WinTimerVec timerVec;
620//  WinTimerDict timerDict;
621//  void registerTimer(WinTimerInfo *t);
622//  void unregisterTimer(WinTimerInfo *t);
623//  void sendTimerEvent(int timerId);
624//
625//  QList<QMSG> queuedUserInputEvents;
626//  QList<QMSG> queuedSocketEvents;
627};
628
629QEventDispatcherPMPrivate::QEventDispatcherPMPrivate()
630    : hab(NULLHANDLE), hmq(NULLHANDLE), interrupt(false)
631{
632}
633
634QEventDispatcherPMPrivate::~QEventDispatcherPMPrivate()
635{
636    auxWnd.setDispatcher(0);
637    auxWnd.destroy();
638    if (hmq != NULLHANDLE) {
639        WinDestroyMsgQueue(hmq);
640        WinTerminate(hab);
641    }
642}
643
644void QEventDispatcherPMPrivate::createMsgQueue()
645{
646    if (hmq == NULLHANDLE) {
647        // first, dynamically switch ("morph") to PM mode if we have been
648        // compiled as the console application. This is necessary to create the
649        // event queue, windows and other PM resources. As a side effect, the
650        // console remains attached after morphing which can be useful for
651        // debugging
652        PPIB ppib;
653        DosGetInfoBlocks(NULL, &ppib);
654        if (ppib->pib_ultype != 3)
655            ppib->pib_ultype = 3;
656        // then create the message queue
657        hab = WinInitialize(0);
658        hmq = WinCreateMsgQueue(hab, 0);
659        if (hmq == NULLHANDLE)
660            qWarning("QEventDispatcherPMPrivate::createMsgQueue: "
661                     "WinCreateMsgQueue failed with 0x%08lX",
662                     WinGetLastError(hab));
663    }
664}
665
666void QEventDispatcherPMPrivate::createAuxWnd()
667{
668    if (auxWnd.hwnd() == NULLHANDLE) {
669        createMsgQueue();
670        auxWnd.setDispatcher(this);
671        auxWnd.create();
672    }
673}
674
675MRESULT QEventDispatcherPMPrivate::AuxWnd::message(ULONG msg, MPARAM mp1, MPARAM mp2)
676{
677    QMSG qmsg = { hwnd(), msg, mp1, mp2 };
678
679    QCoreApplication *app = QCoreApplication::instance();
680    MRESULT result;
681    if (app && app->filterEvent(&qmsg, reinterpret_cast<long *>(&result)))
682        return result;
683
684    switch (msg) {
685        case WM_U_SEM_SELECT: {
686            QSocketNotifier *notifier =
687                QSockSelectThread::getSocketNotifier(LONGFROMMP(mp1));
688                if (notifier) {
689                    QEvent event(QEvent::SockAct);
690                    QCoreApplication::sendEvent(notifier, &event);
691                }
692            break;
693        }
694        case WM_U_SEM_ZEROTIMER: {
695// @todo later
696//          if (numZeroTimers) {
697//              activateZeroTimers();
698//              // repost the message if there are still zero timers left
699//              if (numZeroTimers)
700//                  WinPostMsg(hwnd(), WM_U_SEM_ZEROTIMER, 0, 0);
701//          }
702            break;
703        }
704        case WM_TIMER: {
705// @todo later
706//          USHORT id = SHORT1FROMMP(mp1);
707//          dispatchTimer((uint) id, &qmsg);
708            break;
709        }
710    }
711
712    return FALSE;
713}
714
715// @todo remove
716#if 0
717
718LRESULT CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
719{
720    if (message == WM_NCCREATE) {
721            return true;
722    } else if (message == WM_USER) {
723
724        // socket notifier message
725        MSG msg;
726        msg.hwnd = hwnd;
727        msg.message = message;
728        msg.wParam = wp;
729        msg.lParam = lp;
730
731        QCoreApplication *app = QCoreApplication::instance();
732        long result;
733        if (app && app->filterEvent(&msg, &result))
734            return result;
735
736        int type = -1;
737        switch (WSAGETSELECTEVENT(lp)) {
738        case FD_READ:
739        case FD_CLOSE:
740        case FD_ACCEPT:
741            type = 0;
742            break;
743        case FD_WRITE:
744        case FD_CONNECT:
745            type = 1;
746            break;
747        case FD_OOB:
748            type = 2;
749            break;
750        }
751        if (type >= 0) {
752
753    #ifdef GWLP_USERDATA
754            QEventDispatcherPM *eventDispatcher =
755                (QEventDispatcherPM *) GetWindowLongPtrA(hwnd, GWLP_USERDATA);
756    #else
757            QEventDispatcherPM *eventDispatcher =
758                (QEventDispatcherPM *) GetWindowLongA(hwnd, GWL_USERDATA);
759    #endif
760            if (eventDispatcher) {
761                QEventDispatcherPMPrivate *d = eventDispatcher->d_func();
762                QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
763                QSNDict *dict = sn_vec[type];
764
765                QSockNot *sn = dict ? dict->value(wp) : 0;
766                if (sn) {
767                    QEvent event(QEvent::SockAct);
768                    QCoreApplication::sendEvent(sn->obj, &event);
769                }
770            }
771        }
772        return 0;
773
774    } else if (message == WM_TIMER) {
775
776        MSG msg;
777        msg.hwnd = hwnd;
778        msg.message = message;
779        msg.wParam = wp;
780        msg.lParam = lp;
781
782        QCoreApplication *app = QCoreApplication::instance();
783        Q_ASSERT_X(app, "qt_interal_proc", "Timer fired, but no QCoreApplication");
784        if (!app) {
785            KillTimer(hwnd, wp);
786            return 0;
787        }
788
789        long result;
790        if (app->filterEvent(&msg, &result))
791            return result;
792
793        QEventDispatcherPM *eventDispatcher =
794            qobject_cast<QEventDispatcherPM *>(QAbstractEventDispatcher::instance());
795        Q_ASSERT(eventDispatcher != 0);
796        QEventDispatcherPMPrivate *d = eventDispatcher->d_func();
797        d->sendTimerEvent(wp);
798        return 0;
799    }
800
801    return  DefWindowProc(hwnd, message, wp, lp);
802}
803
804void QEventDispatcherPMPrivate::registerTimer(WinTimerInfo *t)
805{
806    Q_ASSERT(internalHwnd);
807
808    Q_Q(QEventDispatcherPM);
809
810    int ok = 0;
811
812    if (t->interval > 10 || !t->interval || !qtimeSetEvent) {
813        ok = 1;
814        if (!t->interval)  // optimization for single-shot-zero-timer
815            QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
816        else
817            ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0);
818    } else {
819        ok = t->fastTimerId = qtimeSetEvent(t->interval, 1, qt_fast_timer_proc, (DWORD_PTR)t,
820                                            TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
821        if (ok == 0) { // fall back to normal timer if no more multimedia timers avaiable
822            ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0);
823        }
824    }
825
826    if (ok == 0)
827        qErrnoWarning("QEventDispatcherPM::registerTimer: Failed to create a timer");
828}
829
830void QEventDispatcherPMPrivate::unregisterTimer(WinTimerInfo *t)
831{
832    // mark timer as unused
833    if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent)
834        QAbstractEventDispatcherPrivate::releaseTimerId(t->timerId);
835
836    if (t->interval == 0) {
837        QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
838    } else if (t->fastTimerId != 0) {
839        qtimeKillEvent(t->fastTimerId);
840        QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
841    } else if (internalHwnd) {
842        KillTimer(internalHwnd, t->timerId);
843    }
844    delete t;
845}
846
847void QEventDispatcherPMPrivate::sendTimerEvent(int timerId)
848{
849    WinTimerInfo *t = timerDict.value(timerId);
850    if (t && !t->inTimerEvent) {
851        // send event, but don't allow it to recurse
852        t->inTimerEvent = true;
853
854        QTimerEvent e(t->timerId);
855        QCoreApplication::sendEvent(t->obj, &e);
856
857        // timer could have been removed
858        t = timerDict.value(timerId);
859        if (t) {
860            t->inTimerEvent = false;
861        }
862    }
863}
864
865void QEventDispatcherPM::createInternalHwnd()
866{
867    Q_D(QEventDispatcherPM);
868
869    Q_ASSERT(!d->internalHwnd);
870    if (d->internalHwnd)
871        return;
872    d->internalHwnd = qt_create_internal_window(this);
873
874    // register all socket notifiers
875    QList<int> sockets = (d->sn_read.keys().toSet()
876                          + d->sn_write.keys().toSet()
877                          + d->sn_except.keys().toSet()).toList();
878    for (int i = 0; i < sockets.count(); ++i)
879        d->doWsaAsyncSelect(sockets.at(i));
880
881    // start all normal timers
882    for (int i = 0; i < d->timerVec.count(); ++i)
883        d->registerTimer(d->timerVec.at(i));
884}
885
886#endif
887
888QEventDispatcherPM::QEventDispatcherPM(QObject *parent)
889    : QAbstractEventDispatcher(*new QEventDispatcherPMPrivate, parent)
890{
891}
892
893QEventDispatcherPM::~QEventDispatcherPM()
894{
895}
896
897bool QEventDispatcherPM::processEvents(QEventLoop::ProcessEventsFlags flags)
898{
899// @todo later
900#if 0
901    Q_D(QEventDispatcherPM);
902
903    if (!d->internalHwnd)
904        createInternalHwnd();
905
906    d->interrupt = false;
907    emit awake();
908
909    bool canWait;
910    bool retVal = false;
911    do {
912        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
913
914        DWORD waitRet = 0;
915        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
916        QVarLengthArray<MSG> processedTimers;
917        while (!d->interrupt) {
918            MSG msg;
919            bool haveMessage;
920
921            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
922                // process queued user input events
923                haveMessage = true;
924                msg = d->queuedUserInputEvents.takeFirst();
925            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
926                // process queued socket events
927                haveMessage = true;
928                msg = d->queuedSocketEvents.takeFirst();
929            } else {
930                haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE);
931                if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
932                    && ((msg.message >= WM_KEYFIRST
933                         && msg.message <= WM_KEYLAST)
934                        || (msg.message >= WM_MOUSEFIRST
935                            && msg.message <= WM_MOUSELAST)
936                        || msg.message == WM_MOUSEWHEEL)) {
937                    // queue user input events for later processing
938                    haveMessage = false;
939                    d->queuedUserInputEvents.append(msg);
940                }
941                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
942                    && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {
943                    // queue socket events for later processing
944                    haveMessage = false;
945                    d->queuedSocketEvents.append(msg);
946                }
947            }
948            if (haveMessage) {
949                if (msg.message == WM_TIMER) {
950                    // avoid live-lock by keeping track of the timers we've already sent
951                    bool found = false;
952                    for (int i = 0; !found && i < processedTimers.count(); ++i) {
953                        const MSG processed = processedTimers.constData()[i];
954                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
955                    }
956                    if (found)
957                        continue;
958                    processedTimers.append(msg);
959                } else if (msg.message == WM_QUIT) {
960                    if (QCoreApplication::instance())
961                        QCoreApplication::instance()->quit();
962                    return false;
963                }
964
965                if (!filterEvent(&msg)) {
966                    TranslateMessage(&msg);
967                    QT_WA({
968                        DispatchMessage(&msg);
969                    } , {
970                        DispatchMessageA(&msg);
971                    });
972                }
973            } else {
974                // nothing todo so break
975                break;
976            }
977            retVal = true;
978        }
979
980        // still nothing - wait for message or signalled objects
981        QThreadData *data = d->threadData;
982        canWait = (!retVal
983                   && data->canWait
984                   && !d->interrupt
985                   && (flags & QEventLoop::WaitForMoreEvents));
986        if (canWait) {
987            emit aboutToBlock();
988            waitRet = WinGetMsg(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
989            emit awake();
990        }
991    } while (canWait);
992
993    return retVal;
994#else
995    return false;
996#endif
997}
998
999bool QEventDispatcherPM::hasPendingEvents()
1000{
1001    QMSG msg;
1002    return qGlobalPostedEventsCount() || WinPeekMsg(0, &msg, NULL, 0, 0, PM_NOREMOVE);
1003}
1004
1005void QEventDispatcherPM::registerSocketNotifier(QSocketNotifier *notifier)
1006{
1007    Q_ASSERT(notifier);
1008#ifndef QT_NO_DEBUG
1009    int sockfd = notifier->socket();
1010    if (sockfd < 0
1011        || unsigned(sockfd) >= FD_SETSIZE) {
1012        qWarning("QSocketNotifier: Internal error");
1013        return;
1014    } else if (notifier->thread() != thread()
1015               || thread() != QThread::currentThread()) {
1016        qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
1017        return;
1018    }
1019#endif
1020
1021    Q_D(QEventDispatcherPM);
1022    d->createAuxWnd();
1023
1024    QSockSelectThread::addSelect(notifier, d->auxWnd.hwnd());
1025}
1026
1027void QEventDispatcherPM::unregisterSocketNotifier(QSocketNotifier *notifier)
1028{
1029    Q_ASSERT(notifier);
1030#ifndef QT_NO_DEBUG
1031    int sockfd = notifier->socket();
1032    if (sockfd < 0
1033        || unsigned(sockfd) >= FD_SETSIZE) {
1034        qWarning("QSocketNotifier: Internal error");
1035        return;
1036    } else if (notifier->thread() != thread()
1037               || thread() != QThread::currentThread()) {
1038        qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
1039        return;
1040    }
1041#endif
1042
1043    QSockSelectThread::removeSelect(notifier);
1044}
1045
1046void QEventDispatcherPM::registerTimer(int timerId, int interval, QObject *object)
1047{
1048// @todo later
1049#if 0
1050    if (timerId < 1 || interval < 0 || !object) {
1051        qWarning("QEventDispatcherPM::registerTimer: invalid arguments");
1052        return;
1053    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
1054        qWarning("QObject::startTimer: timers cannot be started from another thread");
1055        return;
1056    }
1057
1058    Q_D(QEventDispatcherPM);
1059
1060    register WinTimerInfo *t = new WinTimerInfo;
1061    t->dispatcher = this;
1062    t->timerId  = timerId;
1063    t->interval = interval;
1064    t->obj  = object;
1065    t->inTimerEvent = false;
1066    t->fastTimerId = 0;
1067
1068    if (d->internalHwnd)
1069        d->registerTimer(t);
1070
1071    d->timerVec.append(t);                      // store in timer vector
1072    d->timerDict.insert(t->timerId, t);          // store timers in dict
1073#endif
1074}
1075
1076bool QEventDispatcherPM::unregisterTimer(int timerId)
1077{
1078// @todo later
1079#if 0
1080    if (timerId < 1) {
1081        qWarning("QEventDispatcherPM::unregisterTimer: invalid argument");
1082        return false;
1083    }
1084    QThread *currentThread = QThread::currentThread();
1085    if (thread() != currentThread) {
1086        qWarning("QObject::killTimer: timers cannot be stopped from another thread");
1087        return false;
1088    }
1089
1090    Q_D(QEventDispatcherPM);
1091    if (d->timerVec.isEmpty() || timerId <= 0)
1092        return false;
1093
1094    WinTimerInfo *t = d->timerDict.value(timerId);
1095    if (!t)
1096        return false;
1097
1098    d->timerDict.remove(t->timerId);
1099    d->timerVec.removeAll(t);
1100    d->unregisterTimer(t);
1101    return true;
1102#else
1103    return false;
1104#endif
1105}
1106
1107bool QEventDispatcherPM::unregisterTimers(QObject *object)
1108{
1109// @todo later
1110#if 0
1111    if (!object) {
1112        qWarning("QEventDispatcherPM::unregisterTimers: invalid argument");
1113        return false;
1114    }
1115    QThread *currentThread = QThread::currentThread();
1116    if (object->thread() != thread() || thread() != currentThread) {
1117        qWarning("QObject::killTimers: timers cannot be stopped from another thread");
1118        return false;
1119    }
1120
1121    Q_D(QEventDispatcherPM);
1122    if (d->timerVec.isEmpty())
1123        return false;
1124    register WinTimerInfo *t;
1125    for (int i=0; i<d->timerVec.size(); i++) {
1126        t = d->timerVec.at(i);
1127        if (t && t->obj == object) {                // object found
1128            d->timerDict.remove(t->timerId);
1129            d->timerVec.removeAt(i);
1130            d->unregisterTimer(t);
1131            --i;
1132        }
1133    }
1134    return true;
1135#else
1136    return false;
1137#endif
1138}
1139
1140QList<QEventDispatcherPM::TimerInfo>
1141QEventDispatcherPM::registeredTimers(QObject *object) const
1142{
1143// @todo later
1144#if 0
1145    if (!object) {
1146        qWarning("QEventDispatcherPM:registeredTimers: invalid argument");
1147        return QList<TimerInfo>();
1148    }
1149
1150    Q_D(const QEventDispatcherPM);
1151    QList<TimerInfo> list;
1152    for (int i = 0; i < d->timerVec.size(); ++i) {
1153        const WinTimerInfo *t = d->timerVec.at(i);
1154        if (t && t->obj == object)
1155            list << TimerInfo(t->timerId, t->interval);
1156    }
1157    return list;
1158#else
1159    return QList<TimerInfo>();
1160#endif
1161}
1162
1163void QEventDispatcherPM::wakeUp()
1164{
1165    Q_D(QEventDispatcherPM);
1166    PTIB ptib;
1167    DosGetInfoBlocks(&ptib, NULL);
1168    MQINFO mqinfo;
1169    WinQueryQueueInfo(d->hmq, &mqinfo, sizeof(MQINFO));
1170    if (ptib->tib_ptib2->tib2_ultid != mqinfo.tid)
1171        WinPostQueueMsg(d->hmq, WM_NULL, 0, 0);
1172}
1173
1174void QEventDispatcherPM::interrupt()
1175{
1176    Q_D(QEventDispatcherPM);
1177    d->interrupt = true;
1178    wakeUp();
1179}
1180
1181void QEventDispatcherPM::flush()
1182{
1183}
1184
1185void QEventDispatcherPM::startingUp()
1186{
1187    QSockSelectThread::attachThread();
1188}
1189
1190void QEventDispatcherPM::closingDown()
1191{
1192    QSockSelectThread::detachThread();
1193
1194// @todo remove later
1195#if 0
1196    Q_D(QEventDispatcherPM);
1197
1198    // clean up any timers
1199    while (!d->timerDict.isEmpty())
1200        unregisterTimer((*(d->timerDict.begin()))->timerId);
1201#endif
1202}
1203
1204// @todo remove later
1205#if 0
1206bool QEventDispatcherPM::event(QEvent *e)
1207{
1208    Q_D(QEventDispatcherPM);
1209    if (e->type() == QEvent::ZeroTimerEvent) {
1210        QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
1211        WinTimerInfo *t = d->timerDict.value(zte->timerId());
1212        if (t) {
1213            t->inTimerEvent = true;
1214
1215            QTimerEvent te(zte->timerId());
1216            QCoreApplication::sendEvent(t->obj, &te);
1217
1218            t = d->timerDict.value(zte->timerId());
1219            if (t) {
1220                if (t->interval == 0 && t->inTimerEvent) {
1221                    // post the next zero timer event as long as the timer was not restarted
1222                    QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId()));
1223                }
1224
1225                t->inTimerEvent = false;
1226            }
1227        }
1228        return true;
1229    } else if (e->type() == QEvent::Timer) {
1230        QTimerEvent *te = static_cast<QTimerEvent*>(e);
1231        d->sendTimerEvent(te->timerId());
1232    }
1233    return QAbstractEventDispatcher::event(e);
1234}
1235#endif
1236
1237QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.